CrazyIter_Bin 4 月之前
父節點
當前提交
5cab17c897
共有 100 個文件被更改,包括 0 次插入12609 次删除
  1. 0 3
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/IES.ExamClient.esproj
  2. 0 88
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/app.js
  3. 0 45
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/package.json
  4. 0 1501
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/yarn.lock
  5. 0 23
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/.gitignore
  6. 0 24
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/README.md
  7. 0 5
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/babel.config.js
  8. 0 19
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/jsconfig.json
  9. 0 55
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/package.json
  10. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/public/favicon.ico
  11. 0 17
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/public/index.html
  12. 0 129
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/public/signalr.html
  13. 0 45
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/App.vue
  14. 0 296
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/api/http.js
  15. 0 43
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/api/index.js
  16. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/fengjing.jpg
  17. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/icon/icon_play.png
  18. 0 539
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/demo.css
  19. 0 579
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/demo_index.html
  20. 0 83
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.css
  21. 0 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.js
  22. 0 128
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.json
  23. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.ttf
  24. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.woff
  25. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.woff2
  26. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/image/tmd_logo.png
  27. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/logo.png
  28. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/qrCode.png
  29. 0 63
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/reset.css
  30. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/audio.png
  31. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/excel.png
  32. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/folder.png
  33. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/image.png
  34. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/item.png
  35. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/link.png
  36. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/pdf.png
  37. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/ppt.png
  38. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/unknow.png
  39. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/video.png
  40. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/word.png
  41. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/zip.png
  42. 0 163
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/components/Description.vue
  43. 0 58
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/components/HelloWorld.vue
  44. 0 143
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/components/Privacy.vue
  45. 0 31
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/main.js
  46. 0 60
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/router/router.js
  47. 0 734
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/utils/blobTool.js
  48. 0 490
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/utils/js-fn.js
  49. 0 2249
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/utils/public.js
  50. 0 68
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/utils/signalR.js
  51. 0 141
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/admin/ActivityManage.less
  52. 0 317
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/admin/ActivityManage.vue
  53. 0 526
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/login/Admin.vue
  54. 0 10
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/login/Index.vue
  55. 0 13
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/login/Student.vue
  56. 0 26
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/ActivityAnswer.less
  57. 0 353
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/ActivityAnswer.vue
  58. 0 150
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/ActivityInfo.less
  59. 0 223
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/ActivityInfo.vue
  60. 0 197
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/AudioRecorder.vue
  61. 0 19
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/vue.config.js
  62. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/cert.pem
  63. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/certificate.bat
  64. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/certificate.cer
  65. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/key.pem
  66. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/cert.pem
  67. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/certificate.bat
  68. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/certificate.cer
  69. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/key.pem
  70. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/cert.pem
  71. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/certificate.bat
  72. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/certificate.cer
  73. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/key.pem
  74. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/cert.pem
  75. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/certificate.bat
  76. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/certificate.cer
  77. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/key.pem
  78. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/cert.pem
  79. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/certificate.bat
  80. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/certificate.cer
  81. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/key.pem
  82. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/logo.png
  83. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/cert.pem
  84. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/certificate.bat
  85. 二進制
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/certificate.cer
  86. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/key.pem
  87. 0 104
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/BaseController.cs
  88. 0 416
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/IndexController.cs
  89. 0 651
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/ManageController.cs
  90. 0 6
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/StudentController.cs
  91. 0 23
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/CenterServiceConnectionService.cs
  92. 0 248
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/CustomFileLoggerProvider.cs
  93. 0 59
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/LiteDBFactory.cs
  94. 0 158
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/ServiceInitializer.cs
  95. 0 143
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRCloudClientHub.cs
  96. 0 219
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRHost/SignalRExamServerHub.cs
  97. 0 29
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Filters/AspNetCoreBuilderServiceCollectionExtensions.cs
  98. 0 114
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Filters/AuthTokenAttribute.cs
  99. 0 38
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Helpers/CollectionHelper.cs
  100. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Helpers/Constant.cs

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

+ 0 - 88
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/app.js

@@ -1,88 +0,0 @@
-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');
-} else {
-    console.log('Running on an unknown platform');
-}
-
-//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 createWindow = async () => {
-    try {
-        //await startDotnet();
-        const win = new BrowserWindow({
-            width: 800,
-            height: 600
-            //webPreferences: {
-            //    nodeIntegration: true,
-            //    contextIsolation: false,
-            //},
-        });
-        // 忽略证书错误
-        //win.webContents.session.setCertificateVerifyProc((request, callback) => {
-        //    callback(0); // 允许所有证书
-        //});
-        win.maximize();
-        win.loadURL('https://exam.habook.local:8001');
-    } catch (error) {
-        console.error('Error starting dotnet or loading window:', error);
-    }
-};
-
-// Menu.setApplicationMenu(null);
-
-app.whenReady().then(() => {
-    createWindow();
-
-    app.on('activate', () => {
-        if (BrowserWindow.getAllWindows().length === 0) {
-            createWindow();
-        }
-    });
-});
-
-app.on('window-all-closed', () => {
-    if (process.platform !== 'darwin') {
-        app.quit();
-    }
-});

+ 0 - 45
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/package.json

@@ -1,45 +0,0 @@
-{
-  "name": "examclient",
-  "version": "1.0.0",
-  "description": "",
-  "main": "app.js",
-  "scripts": {
-    "start": "electron .",
-    "test": "echo \"Error: no test specified\" && exit 1",
-    "package": "electron-packager . --platform=win32 --arch=ia32 --out=dist --overwrite",
-    "build": "electron-builder --win --ia32"
-  },
-  "keywords": [],
-  "author": "",
-  "license": "MIT",
-  "devDependencies": {
-    "electron": "12.2.3",
-    "electron-packager": "^17.1.2"
-  },
-  "build": {
-    "electronDownload": {
-      "mirror": "https://npm.taobao.org/mirrors/electron/"
-    },
-    "appId": "exam.habool.local",
-    "productName": "评测局域网教师端",
-    "directories": {
-      "output": "dist"
-    },
-    "win": {
-      "target": "nsis",
-      "sign": false
-    },
-    "nsis": {
-      "runAfterFinish": false
-    },
-    "extraFiles": [
-      {
-        "from": "server",
-        "to": "server",
-        "filter": [
-          "**/*"
-        ]
-      }
-    ]
-  }
-}

File diff suppressed because it is too large
+ 0 - 1501
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/yarn.lock


+ 0 - 23
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/.gitignore

@@ -1,23 +0,0 @@
-.DS_Store
-node_modules
-/dist
-
-
-# local env files
-.env.local
-.env.*.local
-
-# Log files
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-
-# Editor directories and files
-.idea
-.vscode
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?

+ 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 - 5
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/babel.config.js

@@ -1,5 +0,0 @@
-module.exports = {
-  presets: [
-    '@vue/cli-plugin-babel/preset'
-  ]
-}

+ 0 - 19
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/jsconfig.json

@@ -1,19 +0,0 @@
-{
-  "compilerOptions": {
-    "target": "es5",
-    "module": "esnext",
-    "baseUrl": "./",
-    "moduleResolution": "node",
-    "paths": {
-      "@/*": [
-        "src/*"
-      ]
-    },
-    "lib": [
-      "esnext",
-      "dom",
-      "dom.iterable",
-      "scripthost"
-    ]
-  }
-}

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

@@ -1,55 +0,0 @@
-{
-  "name": "ies.examwebview",
-  "version": "0.1.0",
-  "private": true,
-  "scripts": {
-    "serve": "vue-cli-service serve",
-    "build": "vue-cli-service build",
-    "lint": "vue-cli-service lint"
-  },
-  "dependencies": {
-    "@azure/storage-blob": "^12.26.0",
-    "@fingerprintjs/fingerprintjs": "^4.5.1",
-    "@microsoft/signalr": "^8.0.7",
-    "axios": "^1.7.9",
-    "clean-webpack-plugin": "^4.0.0",
-    "compression-webpack-plugin": "^11.1.0",
-    "core-js": "^3.8.3",
-    "element-ui": "^2.15.14",
-    "js-audio-recorder": "^1.0.7",
-    "qrcodejs2": "^0.0.2",
-    "vue": "^2.6.14",
-    "vue-router": "^3.6.5"
-  },
-  "devDependencies": {
-    "@babel/core": "^7.12.16",
-    "@babel/eslint-parser": "^7.12.16",
-    "@vue/cli-plugin-babel": "~5.0.0",
-    "@vue/cli-plugin-eslint": "~5.0.0",
-    "@vue/cli-service": "~5.0.0",
-    "eslint": "^7.32.0",
-    "eslint-plugin-vue": "^8.0.3",
-    "less": "^4.2.1",
-    "less-loader": "^12.2.0",
-    "vue-template-compiler": "^2.6.14"
-  },
-  "eslintConfig": {
-    "root": true,
-    "env": {
-      "node": true
-    },
-    "extends": [
-      "plugin:vue/essential",
-      "eslint:recommended"
-    ],
-    "parserOptions": {
-      "parser": "@babel/eslint-parser"
-    },
-    "rules": {}
-  },
-  "browserslist": [
-    "> 1%",
-    "last 2 versions",
-    "not dead"
-  ]
-}

二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/public/favicon.ico


+ 0 - 17
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/public/index.html

@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<html lang="">
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width,initial-scale=1.0">
-    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
-    <title>局域网评测</title>
-  </head>
-  <body>
-    <noscript>
-      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
-    </noscript>
-    <div id="app"></div>
-    <!-- built files will be auto injected -->
-  </body>
-</html>

+ 0 - 129
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/public/signalr.html

@@ -1,129 +0,0 @@
-<!DOCTYPE html>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-    <meta charset="utf-8" />
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js"></script>
-    <title></title>
-    <style type="text/css">
-        .content {
-            width: 100%;
-            height: 100%;
-            display: flex;
-            justify-content: left;
-            border: solid #4cff00 1px;
-            margin-top: 2rem;
-        }
-
-        .badge {
-            font-size: large;
-            font-weight: bolder;
-            color: aquamarine;
-        }
-
-        #messagesList {
-            width: 100%;
-        }
-    </style>
-</head>
-<body>
-
-    <div class="container">
-        <textarea rows="3" id="text" cols="120" class="text" placeholder="输入要发送的文字信息"></textarea>
-        <button id="send" class="btn btn-primary">发送</button>
-        <div class="content">
-            <ul id="messagesList" class="list-group">
-            </ul>
-        </div>
-    </div>
-    <script>
-        window.onload = function () {
-            const clientid = '9f4ad914254c028f0b724fe0800ee2ab-127.0.0.1';
-            const data = {
-                clientid: clientid,
-                grant_type: 'ies_qrcode_login'
-            }
-            if (data.clientid) {
-                const url = `/signalr/exam?grant_type=${data.grant_type}&clientid=${data.clientid}`
-
-                // 自定义重连策略
-                const reconnectPolicy = {
-                    nextRetryDelayInMilliseconds: function (retryContext) {
-                        const maxRetries = 360;
-                        const retryDelaySeconds = 10;
-
-                        if (retryContext.previousRetryCount >= maxRetries) {
-                            return null; // 达到最大重试次数后不再重连
-                        }
-
-                        return retryDelaySeconds * 1000; // 返回下一次重连的延迟时间(毫秒)
-                    }
-                };
-                const connection = new signalR.HubConnectionBuilder()
-                    .withUrl(url)
-                    .withAutomaticReconnect(reconnectPolicy) // 使用自定义重连策略
-                    .configureLogging(signalR.LogLevel.Information)
-                    .build();
-                connection.start().then(function () {
-                    console.log(`${data.clientid}连接成功......`);
-                    appendContent(`${data.clientid}连接成功......`);
-                });
-                connection.on("ReceiveConnection", (message) => {
-                    console.log('ReceiveConnection-MESSAGE', message);
-                    appendContent(message.content);
-                });
-                connection.on("ReceiveMessage", (message) => {
-                    console.log('ReceiveMessage-MESSAGE', message);
-                    appendContent(message.content, message.clientid);
-                });
-                connection.onreconnecting((error) => {
-                    console.log(`连接断开,正在尝试重新连接... 错误: ${error}`);
-                });
-
-                connection.onreconnected((connectionId) => {
-                    console.log(`重新连接成功,新的连接ID: ${connectionId}`);
-                });
-
-                connection.onclose((error) => {
-                    console.log(`连接关闭,错误: ${error}`);
-                });
-                const btn = document.getElementById("send")
-                btn.addEventListener('click', function () {
-                    const target = document.getElementById('text')
-                    connection.invoke("ReceiveMessage", data.clientid, data.grant_type,target.value)
-                        .then(_ => {
-                            target.value = '';
-                        })
-                        .catch(err => console.error(err.toString()));
-                })
-            }
-        }
-        function appendContent(content, badge) {
-            let li = document.createElement("li");
-            li.className = 'list-group-item';
-            if (badge) {
-                const span = document.createElement('span');
-                span.className = 'badge';
-                span.innerText = badge + ':';
-                li.appendChild(span);
-            }
-            li.innerHTML += content;
-            document.getElementById("messagesList").appendChild(li);
-        }
-
-        //async function start() {
-        //    try {
-        //        await connection.start();
-        //        console.log("已经连接 SignalR Connected.");
-        //    } catch (err) {
-        //        console.log(err);
-        //        setTimeout(start, 5000);
-        //    }
-        //};
-        //connection.onclose(async () => {
-        //    await start();
-        //});
-        //start();
-    </script>
-</body>
-</html>

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

@@ -1,45 +0,0 @@
-<template>
-    <div id="app">
-        <router-view></router-view>
-    </div>
-</template>
-
-<script>
-import HelloWorld from "./components/HelloWorld.vue"
-
-export default {
-    name: "App",
-    components: {
-        HelloWorld,
-    },
-}
-</script>
-
-<style>
-#app {
-    font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",'Microsoft JhengHei',Arial,sans-serif;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-    /* text-align: center;
-    color: #2c3e50;
-    margin-top: 60px; */
-}
-html,
-body,
-#app {
-    height: 100%;
-    margin: 0;
-}
-.el-button:focus {
-    outline: none;
-}
-.el-empty {
-    margin: auto;
-}
-.el-container {
-    height: 100%;
-}
-[class*=" el-icon-"], [class^=el-icon-] {
-    vertical-align: -.125em !important;
-}
-</style>

+ 0 - 296
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/api/http.js

@@ -1,296 +0,0 @@
-import axios from 'axios'
-import { app } from '@/main';
-import { Loading } from 'element-ui';
-
-// 不需携带access_token
-const NO_ACCESS_API = [
-    '/index/device',
-    '/index/login-init',
-    '/index/login-check',
-    '/index/list-schools',
-    '/index/bind-school',
-]
-// 需要携带access_token 不需要auth-token
-const NO_AUTH_API = []
-// 不进行错误提示白名单
-const NO_WARNING = []
-
-
-// token刷新中,挂载的请求
-let requestStack = []
-// 记录token刷新中发起的请求
-function track(prms) {
-    requestStack.push(prms)
-}
-// token刷新完成触发挂起的请求继续请求
-function trigger() {
-    let index = requestStack.length
-    while(index > 0) {
-        requestStack[index - 1]()
-        requestStack.pop()
-        index--
-    }
-}
-
-let refreshing = false //是否刷新token中
-axios.defaults.timeout = 30000 //设置超时时长
-axios.defaults.baseURL = ''
-
-let loading = undefined
-/* Loading.service({
-    lock: true,
-    text: '加载中',
-    background: 'rgba(0, 0, 0, 0.7)'
-}) */
-
-// http request 拦截器
-axios.interceptors.request.use(config => {
-    loading = Loading.service({
-        lock: true,
-        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)) {
-            isNeedAccess = false
-            break
-        }
-    }
-    if(!isNeedAccess) return config
-
-    // 2. 登录后,
-    // 检查操作时间
-    let webEndTime = localStorage.getItem('webEndTime')
-    let time_now = new Date().getTime()
-    if(webEndTime && time_now > webEndTime) {
-        loginOut()
-        sessionStorage.setItem('loginOut', '长时间未操作,重新登录')
-        return
-    }
-    // 检查是否有access_token
-    let access_token = localStorage.getItem('access_token')
-    if(!access_token) {
-        loginOut()
-        sessionStorage.setItem('loginOut', 'token无效')
-        return
-    }
-    // 检查是否快到期
-    let isExpired = checkToken()
-    // token未过期
-    if(!isExpired) {
-        return handleHeader(config)
-    }
-    // 刷新token
-    let handleRefresh = new Promise((resolve, reject) => {
-        track(() => {
-            handleHeader(config)
-            resolve(config)
-        })
-    })
-    sessionStorage.setItem('apiCount', requestStack.length)
-    if(!refreshing) {
-        refreshing = true
-        refreshToken()
-    }
-    return handleRefresh
-}, error => {
-    return Promise.reject(error)
-})
-
-// http response 拦截器
-axios.interceptors.response.use(response => {
-    if(response.data.errCode === 2) {
-        /* router.push({
-            path: '/login',
-            query: {
-                redirect: router.currentRoute.fullPath
-            }
-        }) */
-        console.log('errCode');
-    }
-    // 保存最新的服务端时间
-    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)
-    setTimeout(() => {
-        loading.close()
-    }, 1500)
-    return response
-}, error => {
-    console.log('vbfbbtfnt', error);
-    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) {
-        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'
-        app.$message({
-            type: 'error',
-            message: 'http.error401'
-        })
-    } else if(error.response.status === 500) {
-        app.$message({
-            type: 'error',
-            message: 'http.error500'
-        })
-    } else if(error.response.status === 404) {
-        app.$message({
-            type: 'error',
-            message: 'http.error404'
-        })
-    } else if(error.response.status === 501) {
-        app.$message({
-            type: 'error',
-            message: 'http.error501'
-        })
-    } else {
-        app.$message({
-            type: 'error',
-            message: 'http.error400'
-        })
-    }
-    loading.close()
-    return Promise.reject(error)
-})
-
-function handleHeader(config) {
-    config.headers['Authorization'] = 'Bearer ' + localStorage.getItem('access_token')
-    config.headers['Content-Type'] = 'application/json'
-    config.headers['lang'] = localStorage.getItem('local') || navigator.language.toLowerCase()
-
-    let isNeedAuth = true
-    for (let apiUrl of NO_AUTH_API) {
-        if(config.url.includes(apiUrl)) {
-            console.log('auth-token无效', config)
-            isNeedAuth = false
-            break
-        }
-    }
-    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) {
-        console.log('auth_token失败', config)
-        loginOut()
-        sessionStorage.setItem('loginOut', 'localStorage没有auth_token:auth_token失败,重新登录')
-        return
-    }
-    // 通过验证,设置对应参数
-    config.headers['X-Auth-AuthToken'] = auth_token
-    return config
-}
-
-// 检查token是否快过期
-function checkToken() {
-    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) {
-        return false
-    } else {
-        return true
-    }
-}
-
-// 刷新token
-function refreshToken() {
-    refreshing = true
-    let areaRoute = window.location.pathname.split('/')
-    axios.post('/activity/login-portal', {
-        "route": areaRoute[1],
-        "token": localStorage.getItem('auth_token')
-    }).then(res => {
-        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)
-            // token刷新完成,触发挂载的API
-            trigger()
-            refreshing = false
-        } else {
-            refreshing = false
-            requestStack = []
-            loginOut()
-            sessionStorage.setItem('loginOut', 'Token验证失败')
-        }
-    },err => {
-        refreshing = false
-        requestStack = []
-        loginOut()
-        sessionStorage.setItem('loginOut', 'token刷新失败,退出重新登录')
-    })
-}
-
-// 超时退出重新登录
-function loginOut() {
-    localStorage.clear()
-    console.log('超时退出');
-    // router.push({path: '/home/homePage'})
-    window.location.href = window.location.origin + '/home/homePage'
-}
-
-/**
- * 封装get方法
- * @param url
- * @param data
- * @returns {Promise}
- */
-export function fetch(url, params) {
-    let data = {}
-    data.method = url
-    data.params = params
-    data.lang = localStorage.getItem('local')
-    return new Promise((resolve, reject) => {
-        axios.get(url, data).then(response => {
-            resolve(response.data)
-            app.$message({
-                type: 'success',
-                message: '数据访问成功!'
-            })
-        }).catch(err => {
-            reject(err)
-        })
-    })
-}
-
-/**
- * 封装post请求
- * @param url
- * @param data
- * @returns {Promise}
- */
-export function post(url, params) {
-    let data = {}
-    data.method = url
-    data.params = params
-    data.lang = localStorage.getItem('local')
-    return new Promise((resolve, reject) => {
-        axios.post(url, params).then(response => {
-            if(response) {
-                resolve(response.data)
-            }
-        }, err => {
-            reject(err)
-        })
-    })
-}

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

@@ -1,43 +0,0 @@
-import { fetch, post } from '@/api/http'
-
-export default {
-    /**
-     * 获取当前浏览器信息
-     * @param {String} fp - 浏览器指纹
-     */
-    getDevice: function(data) {
-        return post('/index/device', data)
-    },
-
-    // 登录页面
-    /**
-     * 获取二维码 / 短信验证码
-     * @param {String} type - qrcode(二维码)  smspin(短信验证码)
-     * @param {String} area - 短信验证码:区号
-     * @param {String} to - 短信验证码:手机号
-     */
-    getCode: function(data) {
-        return post('/index/login-init', data)
-    },
-    /**
-     * 二维码登录 / 短信验证码登录
-     * @param {String} type - qrcode(二维码)  smspin(短信验证码)
-     * @param {String} randomCode - 
-     * @param {String} pin_code - 短信验证码
-     * @param {String} account - +区号-手机号
-     */
-    loginCheck: function(data) {
-        return post('/index/login-check', data)
-    },
-    getSchoolList: function(data) {
-        return post('/index/list-schools', 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.ExamServer/ClientApp/src/assets/icon/icon_play.png


+ 0 - 539
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/demo.css

@@ -1,539 +0,0 @@
-/* Logo 字体 */
-@font-face {
-  font-family: "iconfont logo";
-  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
-  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
-    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
-    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
-    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
-}
-
-.logo {
-  font-family: "iconfont logo";
-  font-size: 160px;
-  font-style: normal;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-}
-
-/* tabs */
-.nav-tabs {
-  position: relative;
-}
-
-.nav-tabs .nav-more {
-  position: absolute;
-  right: 0;
-  bottom: 0;
-  height: 42px;
-  line-height: 42px;
-  color: #666;
-}
-
-#tabs {
-  border-bottom: 1px solid #eee;
-}
-
-#tabs li {
-  cursor: pointer;
-  width: 100px;
-  height: 40px;
-  line-height: 40px;
-  text-align: center;
-  font-size: 16px;
-  border-bottom: 2px solid transparent;
-  position: relative;
-  z-index: 1;
-  margin-bottom: -1px;
-  color: #666;
-}
-
-
-#tabs .active {
-  border-bottom-color: #f00;
-  color: #222;
-}
-
-.tab-container .content {
-  display: none;
-}
-
-/* 页面布局 */
-.main {
-  padding: 30px 100px;
-  width: 960px;
-  margin: 0 auto;
-}
-
-.main .logo {
-  color: #333;
-  text-align: left;
-  margin-bottom: 30px;
-  line-height: 1;
-  height: 110px;
-  margin-top: -50px;
-  overflow: hidden;
-  *zoom: 1;
-}
-
-.main .logo a {
-  font-size: 160px;
-  color: #333;
-}
-
-.helps {
-  margin-top: 40px;
-}
-
-.helps pre {
-  padding: 20px;
-  margin: 10px 0;
-  border: solid 1px #e7e1cd;
-  background-color: #fffdef;
-  overflow: auto;
-}
-
-.icon_lists {
-  width: 100% !important;
-  overflow: hidden;
-  *zoom: 1;
-}
-
-.icon_lists li {
-  width: 100px;
-  margin-bottom: 10px;
-  margin-right: 20px;
-  text-align: center;
-  list-style: none !important;
-  cursor: default;
-}
-
-.icon_lists li .code-name {
-  line-height: 1.2;
-}
-
-.icon_lists .icon {
-  display: block;
-  height: 100px;
-  line-height: 100px;
-  font-size: 42px;
-  margin: 10px auto;
-  color: #333;
-  -webkit-transition: font-size 0.25s linear, width 0.25s linear;
-  -moz-transition: font-size 0.25s linear, width 0.25s linear;
-  transition: font-size 0.25s linear, width 0.25s linear;
-}
-
-.icon_lists .icon:hover {
-  font-size: 100px;
-}
-
-.icon_lists .svg-icon {
-  /* 通过设置 font-size 来改变图标大小 */
-  width: 1em;
-  /* 图标和文字相邻时,垂直对齐 */
-  vertical-align: -0.15em;
-  /* 通过设置 color 来改变 SVG 的颜色/fill */
-  fill: currentColor;
-  /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
-      normalize.css 中也包含这行 */
-  overflow: hidden;
-}
-
-.icon_lists li .name,
-.icon_lists li .code-name {
-  color: #666;
-}
-
-/* markdown 样式 */
-.markdown {
-  color: #666;
-  font-size: 14px;
-  line-height: 1.8;
-}
-
-.highlight {
-  line-height: 1.5;
-}
-
-.markdown img {
-  vertical-align: middle;
-  max-width: 100%;
-}
-
-.markdown h1 {
-  color: #404040;
-  font-weight: 500;
-  line-height: 40px;
-  margin-bottom: 24px;
-}
-
-.markdown h2,
-.markdown h3,
-.markdown h4,
-.markdown h5,
-.markdown h6 {
-  color: #404040;
-  margin: 1.6em 0 0.6em 0;
-  font-weight: 500;
-  clear: both;
-}
-
-.markdown h1 {
-  font-size: 28px;
-}
-
-.markdown h2 {
-  font-size: 22px;
-}
-
-.markdown h3 {
-  font-size: 16px;
-}
-
-.markdown h4 {
-  font-size: 14px;
-}
-
-.markdown h5 {
-  font-size: 12px;
-}
-
-.markdown h6 {
-  font-size: 12px;
-}
-
-.markdown hr {
-  height: 1px;
-  border: 0;
-  background: #e9e9e9;
-  margin: 16px 0;
-  clear: both;
-}
-
-.markdown p {
-  margin: 1em 0;
-}
-
-.markdown>p,
-.markdown>blockquote,
-.markdown>.highlight,
-.markdown>ol,
-.markdown>ul {
-  width: 80%;
-}
-
-.markdown ul>li {
-  list-style: circle;
-}
-
-.markdown>ul li,
-.markdown blockquote ul>li {
-  margin-left: 20px;
-  padding-left: 4px;
-}
-
-.markdown>ul li p,
-.markdown>ol li p {
-  margin: 0.6em 0;
-}
-
-.markdown ol>li {
-  list-style: decimal;
-}
-
-.markdown>ol li,
-.markdown blockquote ol>li {
-  margin-left: 20px;
-  padding-left: 4px;
-}
-
-.markdown code {
-  margin: 0 3px;
-  padding: 0 5px;
-  background: #eee;
-  border-radius: 3px;
-}
-
-.markdown strong,
-.markdown b {
-  font-weight: 600;
-}
-
-.markdown>table {
-  border-collapse: collapse;
-  border-spacing: 0px;
-  empty-cells: show;
-  border: 1px solid #e9e9e9;
-  width: 95%;
-  margin-bottom: 24px;
-}
-
-.markdown>table th {
-  white-space: nowrap;
-  color: #333;
-  font-weight: 600;
-}
-
-.markdown>table th,
-.markdown>table td {
-  border: 1px solid #e9e9e9;
-  padding: 8px 16px;
-  text-align: left;
-}
-
-.markdown>table th {
-  background: #F7F7F7;
-}
-
-.markdown blockquote {
-  font-size: 90%;
-  color: #999;
-  border-left: 4px solid #e9e9e9;
-  padding-left: 0.8em;
-  margin: 1em 0;
-}
-
-.markdown blockquote p {
-  margin: 0;
-}
-
-.markdown .anchor {
-  opacity: 0;
-  transition: opacity 0.3s ease;
-  margin-left: 8px;
-}
-
-.markdown .waiting {
-  color: #ccc;
-}
-
-.markdown h1:hover .anchor,
-.markdown h2:hover .anchor,
-.markdown h3:hover .anchor,
-.markdown h4:hover .anchor,
-.markdown h5:hover .anchor,
-.markdown h6:hover .anchor {
-  opacity: 1;
-  display: inline-block;
-}
-
-.markdown>br,
-.markdown>p>br {
-  clear: both;
-}
-
-
-.hljs {
-  display: block;
-  background: white;
-  padding: 0.5em;
-  color: #333333;
-  overflow-x: auto;
-}
-
-.hljs-comment,
-.hljs-meta {
-  color: #969896;
-}
-
-.hljs-string,
-.hljs-variable,
-.hljs-template-variable,
-.hljs-strong,
-.hljs-emphasis,
-.hljs-quote {
-  color: #df5000;
-}
-
-.hljs-keyword,
-.hljs-selector-tag,
-.hljs-type {
-  color: #a71d5d;
-}
-
-.hljs-literal,
-.hljs-symbol,
-.hljs-bullet,
-.hljs-attribute {
-  color: #0086b3;
-}
-
-.hljs-section,
-.hljs-name {
-  color: #63a35c;
-}
-
-.hljs-tag {
-  color: #333333;
-}
-
-.hljs-title,
-.hljs-attr,
-.hljs-selector-id,
-.hljs-selector-class,
-.hljs-selector-attr,
-.hljs-selector-pseudo {
-  color: #795da3;
-}
-
-.hljs-addition {
-  color: #55a532;
-  background-color: #eaffea;
-}
-
-.hljs-deletion {
-  color: #bd2c00;
-  background-color: #ffecec;
-}
-
-.hljs-link {
-  text-decoration: underline;
-}
-
-/* 代码高亮 */
-/* PrismJS 1.15.0
-https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
-/**
- * prism.js default theme for JavaScript, CSS and HTML
- * Based on dabblet (http://dabblet.com)
- * @author Lea Verou
- */
-code[class*="language-"],
-pre[class*="language-"] {
-  color: black;
-  background: none;
-  text-shadow: 0 1px white;
-  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
-  text-align: left;
-  white-space: pre;
-  word-spacing: normal;
-  word-break: normal;
-  word-wrap: normal;
-  line-height: 1.5;
-
-  -moz-tab-size: 4;
-  -o-tab-size: 4;
-  tab-size: 4;
-
-  -webkit-hyphens: none;
-  -moz-hyphens: none;
-  -ms-hyphens: none;
-  hyphens: none;
-}
-
-pre[class*="language-"]::-moz-selection,
-pre[class*="language-"] ::-moz-selection,
-code[class*="language-"]::-moz-selection,
-code[class*="language-"] ::-moz-selection {
-  text-shadow: none;
-  background: #b3d4fc;
-}
-
-pre[class*="language-"]::selection,
-pre[class*="language-"] ::selection,
-code[class*="language-"]::selection,
-code[class*="language-"] ::selection {
-  text-shadow: none;
-  background: #b3d4fc;
-}
-
-@media print {
-
-  code[class*="language-"],
-  pre[class*="language-"] {
-    text-shadow: none;
-  }
-}
-
-/* Code blocks */
-pre[class*="language-"] {
-  padding: 1em;
-  margin: .5em 0;
-  overflow: auto;
-}
-
-:not(pre)>code[class*="language-"],
-pre[class*="language-"] {
-  background: #f5f2f0;
-}
-
-/* Inline code */
-:not(pre)>code[class*="language-"] {
-  padding: .1em;
-  border-radius: .3em;
-  white-space: normal;
-}
-
-.token.comment,
-.token.prolog,
-.token.doctype,
-.token.cdata {
-  color: slategray;
-}
-
-.token.punctuation {
-  color: #999;
-}
-
-.namespace {
-  opacity: .7;
-}
-
-.token.property,
-.token.tag,
-.token.boolean,
-.token.number,
-.token.constant,
-.token.symbol,
-.token.deleted {
-  color: #905;
-}
-
-.token.selector,
-.token.attr-name,
-.token.string,
-.token.char,
-.token.builtin,
-.token.inserted {
-  color: #690;
-}
-
-.token.operator,
-.token.entity,
-.token.url,
-.language-css .token.string,
-.style .token.string {
-  color: #9a6e3a;
-  background: hsla(0, 0%, 100%, .5);
-}
-
-.token.atrule,
-.token.attr-value,
-.token.keyword {
-  color: #07a;
-}
-
-.token.function,
-.token.class-name {
-  color: #DD4A68;
-}
-
-.token.regex,
-.token.important,
-.token.variable {
-  color: #e90;
-}
-
-.token.important,
-.token.bold {
-  font-weight: bold;
-}
-
-.token.italic {
-  font-style: italic;
-}
-
-.token.entity {
-  cursor: help;
-}

+ 0 - 579
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/demo_index.html

@@ -1,579 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <meta charset="utf-8"/>
-  <title>iconfont Demo</title>
-  <link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
-  <link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
-  <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
-  <link rel="stylesheet" href="demo.css">
-  <link rel="stylesheet" href="iconfont.css">
-  <script src="iconfont.js"></script>
-  <!-- jQuery -->
-  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
-  <!-- 代码高亮 -->
-  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
-  <style>
-    .main .logo {
-      margin-top: 0;
-      height: auto;
-    }
-
-    .main .logo a {
-      display: flex;
-      align-items: center;
-    }
-
-    .main .logo .sub-title {
-      margin-left: 0.5em;
-      font-size: 22px;
-      color: #fff;
-      background: linear-gradient(-45deg, #3967FF, #B500FE);
-      -webkit-background-clip: text;
-      -webkit-text-fill-color: transparent;
-    }
-  </style>
-</head>
-<body>
-  <div class="main">
-    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
-      <img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
-      
-    </a></h1>
-    <div class="nav-tabs">
-      <ul id="tabs" class="dib-box">
-        <li class="dib active"><span>Unicode</span></li>
-        <li class="dib"><span>Font class</span></li>
-        <li class="dib"><span>Symbol</span></li>
-      </ul>
-      
-      <a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4795944" target="_blank" class="nav-more">查看项目</a>
-      
-    </div>
-    <div class="tab-container">
-      <div class="content unicode" style="display: block;">
-          <ul class="icon_lists dib-box">
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe647;</span>
-                <div class="name">切换</div>
-                <div class="code-name">&amp;#xe647;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe806;</span>
-                <div class="name">激活</div>
-                <div class="code-name">&amp;#xe806;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe809;</span>
-                <div class="name">激活</div>
-                <div class="code-name">&amp;#xe809;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe71f;</span>
-                <div class="name">物品-书笔</div>
-                <div class="code-name">&amp;#xe71f;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe6b6;</span>
-                <div class="name">意见反馈、记笔记</div>
-                <div class="code-name">&amp;#xe6b6;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe600;</span>
-                <div class="name">错误</div>
-                <div class="code-name">&amp;#xe600;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe619;</span>
-                <div class="name">勾</div>
-                <div class="code-name">&amp;#xe619;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe61b;</span>
-                <div class="name">勾选</div>
-                <div class="code-name">&amp;#xe61b;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe650;</span>
-                <div class="name">勾</div>
-                <div class="code-name">&amp;#xe650;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe714;</span>
-                <div class="name">勾1</div>
-                <div class="code-name">&amp;#xe714;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe62e;</span>
-                <div class="name">叉叉</div>
-                <div class="code-name">&amp;#xe62e;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe620;</span>
-                <div class="name">退出</div>
-                <div class="code-name">&amp;#xe620;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe7ed;</span>
-                <div class="name">退出</div>
-                <div class="code-name">&amp;#xe7ed;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe61c;</span>
-                <div class="name">勾勾</div>
-                <div class="code-name">&amp;#xe61c;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe7d3;</span>
-                <div class="name">点击</div>
-                <div class="code-name">&amp;#xe7d3;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe6bb;</span>
-                <div class="name">已完成</div>
-                <div class="code-name">&amp;#xe6bb;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon element-icons">&#xe666;</span>
-                <div class="name">未作答</div>
-                <div class="code-name">&amp;#xe666;</div>
-              </li>
-          
-          </ul>
-          <div class="article markdown">
-          <h2 id="unicode-">Unicode 引用</h2>
-          <hr>
-
-          <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
-          <ul>
-            <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
-            <li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
-          </ul>
-          <blockquote>
-            <p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
-          </blockquote>
-          <p>Unicode 使用步骤如下:</p>
-          <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
-<pre><code class="language-css"
->@font-face {
-  font-family: 'element-icons';
-  src: url('iconfont.woff2?t=1737019993465') format('woff2'),
-       url('iconfont.woff?t=1737019993465') format('woff'),
-       url('iconfont.ttf?t=1737019993465') format('truetype');
-}
-</code></pre>
-          <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
-<pre><code class="language-css"
->.element-icons {
-  font-family: "element-icons" !important;
-  font-size: 16px;
-  font-style: normal;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-}
-</code></pre>
-          <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
-<pre>
-<code class="language-html"
->&lt;span class="element-icons"&gt;&amp;#x33;&lt;/span&gt;
-</code></pre>
-          <blockquote>
-            <p>"element-icons" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
-          </blockquote>
-          </div>
-      </div>
-      <div class="content font-class">
-        <ul class="icon_lists dib-box">
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-qiehuan"></span>
-            <div class="name">
-              切换
-            </div>
-            <div class="code-name">.el-icon-qiehuan
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-jihuo"></span>
-            <div class="name">
-              激活
-            </div>
-            <div class="code-name">.el-icon-jihuo
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-jihuo1"></span>
-            <div class="name">
-              激活
-            </div>
-            <div class="code-name">.el-icon-jihuo1
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-my-note"></span>
-            <div class="name">
-              物品-书笔
-            </div>
-            <div class="code-name">.el-icon-my-note
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-myNote"></span>
-            <div class="name">
-              意见反馈、记笔记
-            </div>
-            <div class="code-name">.el-icon-myNote
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-cuowu"></span>
-            <div class="name">
-              错误
-            </div>
-            <div class="code-name">.el-icon-cuowu
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-gou"></span>
-            <div class="name">
-              勾
-            </div>
-            <div class="code-name">.el-icon-gou
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-gouxuan"></span>
-            <div class="name">
-              勾选
-            </div>
-            <div class="code-name">.el-icon-gouxuan
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-gou1"></span>
-            <div class="name">
-              勾
-            </div>
-            <div class="code-name">.el-icon-gou1
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-gou11"></span>
-            <div class="name">
-              勾1
-            </div>
-            <div class="code-name">.el-icon-gou11
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-chacha"></span>
-            <div class="name">
-              叉叉
-            </div>
-            <div class="code-name">.el-icon-chacha
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-tuichu"></span>
-            <div class="name">
-              退出
-            </div>
-            <div class="code-name">.el-icon-tuichu
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-tuichu2"></span>
-            <div class="name">
-              退出
-            </div>
-            <div class="code-name">.el-icon-tuichu2
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-gougou1"></span>
-            <div class="name">
-              勾勾
-            </div>
-            <div class="code-name">.el-icon-gougou1
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-dianji"></span>
-            <div class="name">
-              点击
-            </div>
-            <div class="code-name">.el-icon-dianji
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-yiwancheng"></span>
-            <div class="name">
-              已完成
-            </div>
-            <div class="code-name">.el-icon-yiwancheng
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon element-icons el-icon-weizuoda"></span>
-            <div class="name">
-              未作答
-            </div>
-            <div class="code-name">.el-icon-weizuoda
-            </div>
-          </li>
-          
-        </ul>
-        <div class="article markdown">
-        <h2 id="font-class-">font-class 引用</h2>
-        <hr>
-
-        <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
-        <p>与 Unicode 使用方式相比,具有如下特点:</p>
-        <ul>
-          <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
-          <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
-        </ul>
-        <p>使用步骤如下:</p>
-        <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
-<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
-</code></pre>
-        <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
-<pre><code class="language-html">&lt;span class="element-icons el-icon-xxx"&gt;&lt;/span&gt;
-</code></pre>
-        <blockquote>
-          <p>"
-            element-icons" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
-        </blockquote>
-      </div>
-      </div>
-      <div class="content symbol">
-          <ul class="icon_lists dib-box">
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-qiehuan"></use>
-                </svg>
-                <div class="name">切换</div>
-                <div class="code-name">#el-icon-qiehuan</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-jihuo"></use>
-                </svg>
-                <div class="name">激活</div>
-                <div class="code-name">#el-icon-jihuo</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-jihuo1"></use>
-                </svg>
-                <div class="name">激活</div>
-                <div class="code-name">#el-icon-jihuo1</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-my-note"></use>
-                </svg>
-                <div class="name">物品-书笔</div>
-                <div class="code-name">#el-icon-my-note</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-myNote"></use>
-                </svg>
-                <div class="name">意见反馈、记笔记</div>
-                <div class="code-name">#el-icon-myNote</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-cuowu"></use>
-                </svg>
-                <div class="name">错误</div>
-                <div class="code-name">#el-icon-cuowu</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-gou"></use>
-                </svg>
-                <div class="name">勾</div>
-                <div class="code-name">#el-icon-gou</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-gouxuan"></use>
-                </svg>
-                <div class="name">勾选</div>
-                <div class="code-name">#el-icon-gouxuan</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-gou1"></use>
-                </svg>
-                <div class="name">勾</div>
-                <div class="code-name">#el-icon-gou1</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-gou11"></use>
-                </svg>
-                <div class="name">勾1</div>
-                <div class="code-name">#el-icon-gou11</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-chacha"></use>
-                </svg>
-                <div class="name">叉叉</div>
-                <div class="code-name">#el-icon-chacha</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-tuichu"></use>
-                </svg>
-                <div class="name">退出</div>
-                <div class="code-name">#el-icon-tuichu</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-tuichu2"></use>
-                </svg>
-                <div class="name">退出</div>
-                <div class="code-name">#el-icon-tuichu2</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-gougou1"></use>
-                </svg>
-                <div class="name">勾勾</div>
-                <div class="code-name">#el-icon-gougou1</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-dianji"></use>
-                </svg>
-                <div class="name">点击</div>
-                <div class="code-name">#el-icon-dianji</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-yiwancheng"></use>
-                </svg>
-                <div class="name">已完成</div>
-                <div class="code-name">#el-icon-yiwancheng</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#el-icon-weizuoda"></use>
-                </svg>
-                <div class="name">未作答</div>
-                <div class="code-name">#el-icon-weizuoda</div>
-            </li>
-          
-          </ul>
-          <div class="article markdown">
-          <h2 id="symbol-">Symbol 引用</h2>
-          <hr>
-
-          <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
-            这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
-          <ul>
-            <li>支持多色图标了,不再受单色限制。</li>
-            <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
-            <li>兼容性较差,支持 IE9+,及现代浏览器。</li>
-            <li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
-          </ul>
-          <p>使用步骤如下:</p>
-          <h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
-<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
-</code></pre>
-          <h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
-<pre><code class="language-html">&lt;style&gt;
-.icon {
-  width: 1em;
-  height: 1em;
-  vertical-align: -0.15em;
-  fill: currentColor;
-  overflow: hidden;
-}
-&lt;/style&gt;
-</code></pre>
-          <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
-<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
-  &lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
-&lt;/svg&gt;
-</code></pre>
-          </div>
-      </div>
-
-    </div>
-  </div>
-  <script>
-  $(document).ready(function () {
-      $('.tab-container .content:first').show()
-
-      $('#tabs li').click(function (e) {
-        var tabContent = $('.tab-container .content')
-        var index = $(this).index()
-
-        if ($(this).hasClass('active')) {
-          return
-        } else {
-          $('#tabs li').removeClass('active')
-          $(this).addClass('active')
-
-          tabContent.hide().eq(index).fadeIn()
-        }
-      })
-    })
-  </script>
-</body>
-</html>

+ 0 - 83
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.css

@@ -1,83 +0,0 @@
-@font-face {
-  font-family: "element-icons"; /* Project id 4795944 */
-  src: url('iconfont.woff2?t=1737019993465') format('woff2'),
-       url('iconfont.woff?t=1737019993465') format('woff'),
-       url('iconfont.ttf?t=1737019993465') format('truetype');
-}
-
-.element-icons {
-  font-family: "element-icons" !important;
-  font-size: 16px;
-  font-style: normal;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-}
-
-.el-icon-qiehuan:before {
-  content: "\e647";
-}
-
-.el-icon-jihuo:before {
-  content: "\e806";
-}
-
-.el-icon-jihuo1:before {
-  content: "\e809";
-}
-
-.el-icon-my-note:before {
-  content: "\e71f";
-}
-
-.el-icon-myNote:before {
-  content: "\e6b6";
-}
-
-.el-icon-cuowu:before {
-  content: "\e600";
-}
-
-.el-icon-gou:before {
-  content: "\e619";
-}
-
-.el-icon-gouxuan:before {
-  content: "\e61b";
-}
-
-.el-icon-gou1:before {
-  content: "\e650";
-}
-
-.el-icon-gou11:before {
-  content: "\e714";
-}
-
-.el-icon-chacha:before {
-  content: "\e62e";
-}
-
-.el-icon-tuichu:before {
-  content: "\e620";
-}
-
-.el-icon-tuichu2:before {
-  content: "\e7ed";
-}
-
-.el-icon-gougou1:before {
-  content: "\e61c";
-}
-
-.el-icon-dianji:before {
-  content: "\e7d3";
-}
-
-.el-icon-yiwancheng:before {
-  content: "\e6bb";
-}
-
-.el-icon-weizuoda:before {
-  content: "\e666";
-}
-

File diff suppressed because it is too large
+ 0 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.js


+ 0 - 128
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.json

@@ -1,128 +0,0 @@
-{
-  "id": "4795944",
-  "name": "ExamServer",
-  "font_family": "element-icons",
-  "css_prefix_text": "el-icon-",
-  "description": "",
-  "glyphs": [
-    {
-      "icon_id": "9568050",
-      "name": "切换",
-      "font_class": "qiehuan",
-      "unicode": "e647",
-      "unicode_decimal": 58951
-    },
-    {
-      "icon_id": "10082384",
-      "name": "激活",
-      "font_class": "jihuo",
-      "unicode": "e806",
-      "unicode_decimal": 59398
-    },
-    {
-      "icon_id": "10087237",
-      "name": "激活",
-      "font_class": "jihuo1",
-      "unicode": "e809",
-      "unicode_decimal": 59401
-    },
-    {
-      "icon_id": "6536038",
-      "name": "物品-书笔",
-      "font_class": "my-note",
-      "unicode": "e71f",
-      "unicode_decimal": 59167
-    },
-    {
-      "icon_id": "24591518",
-      "name": "意见反馈、记笔记",
-      "font_class": "myNote",
-      "unicode": "e6b6",
-      "unicode_decimal": 59062
-    },
-    {
-      "icon_id": "1108",
-      "name": "错误",
-      "font_class": "cuowu",
-      "unicode": "e600",
-      "unicode_decimal": 58880
-    },
-    {
-      "icon_id": "386336",
-      "name": "勾",
-      "font_class": "gou",
-      "unicode": "e619",
-      "unicode_decimal": 58905
-    },
-    {
-      "icon_id": "579391",
-      "name": "勾选",
-      "font_class": "gouxuan",
-      "unicode": "e61b",
-      "unicode_decimal": 58907
-    },
-    {
-      "icon_id": "782338",
-      "name": "勾",
-      "font_class": "gou1",
-      "unicode": "e650",
-      "unicode_decimal": 58960
-    },
-    {
-      "icon_id": "844603",
-      "name": "勾1",
-      "font_class": "gou11",
-      "unicode": "e714",
-      "unicode_decimal": 59156
-    },
-    {
-      "icon_id": "8988248",
-      "name": "叉叉",
-      "font_class": "chacha",
-      "unicode": "e62e",
-      "unicode_decimal": 58926
-    },
-    {
-      "icon_id": "6626202",
-      "name": "退出",
-      "font_class": "tuichu",
-      "unicode": "e620",
-      "unicode_decimal": 58912
-    },
-    {
-      "icon_id": "16921565",
-      "name": "退出",
-      "font_class": "tuichu2",
-      "unicode": "e7ed",
-      "unicode_decimal": 59373
-    },
-    {
-      "icon_id": "17622335",
-      "name": "勾勾",
-      "font_class": "gougou1",
-      "unicode": "e61c",
-      "unicode_decimal": 58908
-    },
-    {
-      "icon_id": "15900229",
-      "name": "点击",
-      "font_class": "dianji",
-      "unicode": "e7d3",
-      "unicode_decimal": 59347
-    },
-    {
-      "icon_id": "17814604",
-      "name": "已完成",
-      "font_class": "yiwancheng",
-      "unicode": "e6bb",
-      "unicode_decimal": 59067
-    },
-    {
-      "icon_id": "6910350",
-      "name": "未作答",
-      "font_class": "weizuoda",
-      "unicode": "e666",
-      "unicode_decimal": 58982
-    }
-  ]
-}

二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.ttf


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.woff


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.woff2


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/image/tmd_logo.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/logo.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/qrCode.png


+ 0 - 63
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/reset.css

@@ -1,63 +0,0 @@
-/* http://meyerweb.com/eric/tools/css/reset/ 
-   v2.0 | 20110126
-   License: none (public domain)
-*/
-
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, embed, 
-figure, figcaption, footer, header, hgroup, 
-menu, nav, output, ruby, section, summary,
-time, mark, audio, video {
-	margin: 0;
-	padding: 0;
-	border: 0;
-	/*font-size: 100%;*/
-	/*font: inherit;*/
-	vertical-align: baseline;
-}
-/* HTML5 display-role reset for older browsers */
-article, aside, details, figcaption, figure, 
-footer, header, hgroup, menu, nav, section {
-	display: block;
-}
-body {
-	line-height: 1.5;
-	color: #515a6e;
-	font-size: 14px;
-}
-ol, ul {
-	list-style: none;
-}
-blockquote, q {
-	quotes: none;
-}
-blockquote:before, blockquote:after,
-q:before, q:after {
-	content: '';
-	content: none;
-}
-table {
-	border-collapse: collapse;
-	border-spacing: 0;
-}
-a {
-	text-decoration: none;
-  	outline: none;
-}
-
-a {
-    -moz-transition: color .2s ease-in-out;
-    -webkit-transition: color .2s ease-in-out;
-    -ms-transition: color .2s ease-in-out;
-    transition: color .2s ease-in-out;
-    color: #2a6abb;
-    text-decoration: none;
-}

二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/audio.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/excel.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/folder.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/image.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/item.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/link.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/pdf.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/ppt.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/unknow.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/video.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/word.png


二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/zip.png


+ 0 - 163
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/components/Description.vue

@@ -1,163 +0,0 @@
-<template>
-    <div>
-        <div class="wrapper style1 first">
-            <h1>醍摩豆智慧教育服务</h1>
-        </div>
-        <div class="wrapper style3">
-            <div class="container">
-                <div style="text-align: left;">
-                    <h3>醍摩豆(TEAM Model)帐号服务条款</h3>
-                    <p>本政策适用于「醍摩豆(成都)信息技术有限公司」所经营的「醍摩豆(TEAM Model)智慧教育」服务,包括。为保护使用者所提供的个人资料,并保护线上隐私权,故作以下条款声明:</p>
-                    <ul>
-                        <li style="color: #333;margin-left: 20px;">使用者资料之取得<br/>
-                            <span>当使用者要求加入会员、在线购物、下载文件、讨论区发表新讯息时,本网站可能会要求使用者登录个人资料,以便于和使用者联系、完成交易或提供服务。在这些情况下,本网站将明白告知使用者此等事实,不会在使用者不知情的情况下取得这些资料。</span>
-                        </li>
-                        <li style="color: #333;margin-left: 20px;">使用者资料的使用<br/>
-                            <span>本网站经过使用者同意所登录之个人基本资料,只做为在线交易、与会员互动、讯息传递、内部分析或商业服务用途,除非事先经本人同意或符合相关法律规定,否则绝不提供予任何第三人使用或移作其他用途,并善尽管理人的注意义务。假如使用者不愿再接收本网站所寄发关于网站讯息的电子邮件,请即刻通知本网站,本网站将不再寄送。</span>
-                        </li>
-                        <li style="color: #333;margin-left: 20px;">个人资料的查询及更改<br/>
-                            <span>使用者可随时利用账号和密码,查询或更改所输入的个人基本资料。但交易记录、讨论区资料,则不在此范围内。</span>
-                        </li>
-                        <li style="color: #333;margin-left: 20px;">网站的链接<br/>
-                            <span>为了网站内容所需,本网站的网页可能包含其它非「醍摩豆(TEAM Model)智慧教育网站」相关网站或网页的链接,对于这些网站或网页,其内容或隐私权政策,均与本站无关,请使用者自行判断其安全性。</span>
-                        </li>
-                        <li style="color: #333;margin-left: 20px;">使用者发布内容规范<br/>
-                            <p>1、本条所述内容是指使用者在使用我们的产品/服务的过程中所制作、上传、复制、发布、传播的任何内容,包括但不限于账号头像、名称、邮箱等注册信息以及认证资料,或文字、语音、图片、视频、图文等发送、回复或自动回复消息和相关链接页面,以及其他使用产品/服务所产生的内容。</p>
-                            <p>2、使用者不得利用我们的产品/服务制作、上传、复制、发布、传播如下法律、法规和政策禁止的内容:<br/>
-                                (1) 反对宪法所确定的基本原则的; <br/>
-                                (2) 危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的; <br/>
-                                (3) 损害国家荣誉和利益的; <br/>
-                                (4) 煽动民族仇恨、民族歧视,破坏民族团结的; <br/>
-                                (5) 破坏国家宗教政策,宣扬邪教和封建迷信的; <br/>
-                                (6) 散布谣言,扰乱社会秩序,破坏社会稳定的; <br/>
-                                (7) 散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的; <br/>
-                                (8) 侮辱或者诽谤他人,侵害他人合法权益的; <br/>
-                                (9) 含有法律、行政法规禁止的其他内容的信息。
-                            </p>
-                            <p>3、使用者不得利用我们的产品/服务制作、上传、复制、发布、传播如下干扰“服务”正常运营,以及侵犯其他用户或第三方合法权益的内容:<br/>
-                                (1) 含有任何性或性暗示的;<br/>
-                                (2) 含有辱骂、恐吓、威胁内容的; <br/>
-                                (3) 含有骚扰、垃圾广告、恶意信息、诱骗信息的; <br/>
-                                (4) 涉及他人隐私、个人信息或资料的;<br/>
-                                (5) 侵害他人名誉权、肖像权、知识产权、商业秘密等合法权利的;<br/>
-                                (6) 含有其他干扰本服务正常运营和侵犯其他用户或第三方合法权益内容的信息。
-                            </p>
-                        </li>
-                    </ul>
-                    <h3>醍摩豆产品权限获取说明</h3>
-                    <p>我们在此说明当您使用我们的网站、软件、APP及服务 (以下简称为「服务」) 时,我们是如何获取您的个人资料。</p>
-                    <ul>
-                        <li style="color: #333;margin-left: 20px;">醍摩豆帐户资讯读访问权限:当您使用我们的服务,并以您的醍摩豆账号登入时,我们会读取与您醍摩豆账号相关的个人信息,其中包括手机或邮箱。手机和邮箱需要任选其一,它们会成为您账号的绑定密码安全保护。</li>
-                        <li style="color: #333;margin-left: 20px;">装置镜头使用权限:当您欲使用我们的服务扫码登入HiTeach、官网以及醍摩豆相关的平台网站时,在您主动授权后,我们会开启您装置的相机镜头进行此动作。如您选择不开启此权限,您将无法使用与此权限相关的特定功能,但不影响使用其他服务。</li>
-                        <li style="color: #333;margin-left: 20px;">装置录影录音使用权限:当您欲使用我们的服务录制一段即时影片时,在您主动授权后,我们会开启您装置的相机镜头进行此动作。如您选择不开启此权限,您将无法使用与此权限相关的特定功能,但不影响使用其他服务。</li>
-                        <li style="color: #333;margin-left: 20px;">装置相片及影片权限:当您欲使用我们的服务发送图片/影音时,在您主动授权后,我们会开启您装置的相册进行此动作。如您选择不开启此权限,您将无法使用与此权限相关的特定功能,但不影响使用其他服务。</li>
-                        <li style="color: #333;margin-left: 20px;">装置档案使用权限:当您欲使用我们的服务发送档案时,在您主动授权后,我们会开启您装置的档案夹进行此动作。如您选择不开启此权限,您将无法使用与此权限相关的特定功能,但不影响使用其他服务。</li>
-                    </ul>
-                    <p>对于上述您的个人资料,在您没有主动授权或主动上传的情况下,我们不会储存您装置上的任何影音/图片/文件。</p>
-
-                    <h3>醍摩豆账号服务说明</h3>
-                    <p>醍摩豆账号是您使用网奕资讯HABOOK旗下所有醍摩豆(TEAM Model)服务及应用程式时所需要的使用者系统,注册醍摩豆账号,您可以:</p>
-                    <ul>
-                        <li style="color: #333;margin-left: 20px;">登入IES云服务、苏格拉底平台、教师专业成长报名系统等,以醍摩豆账号为认证的所有线上教学应用。</li>
-                        <li style="color: #333;margin-left: 20px;">登入教室端HiTeach,在教室使用最受欢迎的专业教学互动系统。</li>
-                        <li style="color: #333;margin-left: 20px;">登入HiTA APP、AClass ONE,使用便利的教室端应用,及学生配合老师教学即时互动的应用。</li>
-                        <li style="color: #333;margin-left: 20px;">登入苏格拉底智慧观课Web APP、IRS评量互动Web APP,参与教室议课与课堂即时互动。</li>
-                        <li style="color: #333;margin-left: 20px;">登入HABOOK官网,即时了解最新智慧教育最新动态、产品支持、专业资讯、培训活动等。</li>
-                    </ul>
-                    <p>如果您使用过上述任何一个产品,即表示您只要拥有了醍摩豆账号,您的账号和密码可以用来登入HABOOK推出的任何醍摩豆服务与应用。</p>
-                    <p>您注册时绑定的信箱或手机、产生的醍摩豆ID、都可以作为醍摩豆账号。</p>
-
-                    <p>醍摩豆账号可以透过绑定Google、Facebook、WeChat等快速登入及注册,并且仅要求e-mail、名称作为账号及名称显示,不会额外要求其他任何资讯及应用。</p>
-                    <p>想要成为专业的智慧教育专家与学习者吗,快来注册醍摩豆账号吧!</p>
-                    <h4 style="color: #333;">醍摩豆账号须知:</h4>
-                    <p>年龄限制:<br/>要符合醍摩豆帐户的注册资格,您必须至少年满13岁。</p>
-                    <p>账号安全性:<br/>在注册过程中,我们要求您提供部分个人资料,其中包括手机或信箱。信箱和手机需要任选其一,它们会成为您账号的绑定密码安全保护,主要用于找回密码和修改密码安全保护,保护账号安全:</p>
-                    <p>手机号码:<br/>手机号码是一种最简单且最可靠的账号保护方式。由于您的手机是您随身携带的,因此如果您忘记了密码,我们就可以提供一些较为简单的方法帮助您重新浏览账号。并且我们可以透过验证您的手机号码阻止对您账号的非正常浏览。</p>
-                    <p>信箱地址:<br/>电子信箱地址具有长期固定的特征,不易遗失或变更。提供此地址,有助于保护您账号安全和找回密码,同时也为醍摩豆服务传送通知提供媒介。并且我们可以透过验证您的信箱地址阻止对您账号的非正常浏览。</p>
-                    <p>您可以:<br/>
-                    - 更换未绑定醍摩豆账号的手机(信箱)重新绑定。<br/>
-                    - 使用已绑定醍摩豆账号的手机(信箱)直接登入。</p>
-                    <p>由于手机和信箱的遗失或更换,导致不可用时,您可以透过更换手机(信箱)重新绑定。</p>
-                    <p>删除账号意味着账号所有相关资讯将永久遗失,包括使用者名称、密码、服务数据、云同步资讯等。账号将不复存在,删除后无法再找回账号,是危险操作,需慎重!</p>
-                </div>
-            </div>
-            <footer>
-                <a href="/privacy">隐私权政策</a><br/>
-                Copyright © 2019 HABOOK Group. All rights reserved.
-            </footer>
-        </div>
-    </div>
-</template>
-
-<script>
-export default {}
-</script>
-
-<style scoped>
-h1, h2, h3, h4, h5, h6 {
-    color: #3e3e3e;
-}
-
-p, ul {
-    margin-bottom: 2em;
-}
-
-ul {
-    list-style-type: disc;
-    margin-left: 35px;
-}
-
-.wrapper {
-    box-shadow: inset 0px 1px 0px 0px rgba(0, 0, 0, 0.05), inset 0px 2px 3px 0px rgba(0, 0, 0, 0.1);
-    text-align: center;
-    padding: 1.5em 0;
-    color: #000;
-    font-family: "Noto Sans", 'Noto Sans TC', 'Noto Sans SC', sans-serif;
-    font-weight: 300;
-    font-size: 1.22em;
-}
-
-.wrapper.first {
-    box-shadow: none;
-}
-
-.wrapper.style1 {
-    background-image: none;
-    background-color: #2a6abb;
-}
-
-.wrapper.style1 h1 {
-    color: #fff;
-    font-size: 3em;
-}
-
-.wrapper.style3 {
-    background-color: #f4f4f4;
-    line-height: 1.85em;
-}
-
-.wrapper.style3 .container {
-    width: 960px;
-    margin-left: auto;
-    margin-right: auto;
-}
-
-.wrapper.style3 .container h3 {
-    font-size: 1.8em;
-}
-
-@media screen and (min-width: 737px) {
-    h1, h2, h3, h4, h5, h6, h7 {
-        margin: 0 0 0.75em 0;
-    }
-
-    .wrapper.first {
-        padding-top: 3em;
-        padding-bottom: 1.7em;
-    }
-
-    .wrapper.style3 .container {
-        width: 1250px;
-    }
-}
-</style>

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

File diff suppressed because it is too large
+ 0 - 143
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/components/Privacy.vue


+ 0 - 31
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/main.js

@@ -1,31 +0,0 @@
-import Vue from 'vue'
-import App from './App.vue'
-import router from './router/router'
-import apiTools from '@/api'
-import { fetch, post } from '@/api/http'
-import tools from '@/utils/public.js'
-
-import ElementUI from 'element-ui'
-import 'element-ui/lib/theme-chalk/index.css'
-import axios from 'axios'
-import '@/assets/reset.css'
-import "@/assets/iconfont/iconfont.css"
-
-Vue.use(ElementUI)
-Vue.config.productionTip = false
-
-Vue.prototype.$api = apiTools
-Vue.prototype.$axios = axios
-Vue.prototype.$tools = tools
-Vue.prototype.$post = post
-Vue.prototype.$get = fetch
-
-const app = new Vue({
-    el: '#app',
-    router,
-    ...App
-})
-
-export {
-    app
-}

+ 0 - 60
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/router/router.js

@@ -1,60 +0,0 @@
-import Vue from 'vue'
-import VueRouter from 'vue-router'
-
-Vue.use(VueRouter)
-
-const routes = [
-    {
-        path: '/',
-        redirect: '/login/admin',
-    },
-    {
-        path: '/login',
-        component: () => import("@/view/login/Index.vue"),
-        children: [
-            {
-                path: 'admin',
-                name: 'admin',
-                component: () => import('@/view/login/Admin.vue')
-            },
-            {
-                path: 'student',
-                name: 'student',
-                component: () => import('@/view/login/Student.vue')
-            },
-        ]
-    },
-    {
-        path: '/admin',
-        name: 'admin',
-        component: () => import('@/view/admin/ActivityManage.vue')
-    },
-    {
-        path: '/info',
-        name: 'info',
-        component: () => import('@/view/student/ActivityInfo.vue')
-    },
-    {
-        path: '/studentAns',
-        name: 'studentAns',
-        component: () => import('@/view/student/ActivityAnswer.vue')
-    },
-    {
-        path: '/description',
-        name: 'description',
-        component: () => import('@/components/Description.vue')
-    },
-    {
-        path: '/privacy',
-        name: 'privacy',
-        component: () => import('@/components/Privacy.vue')
-    },
-]
-
-const router = new VueRouter({
-    mode: 'history',
-    base: process.env.BASE_URL,
-    routes
-})
-
-export default router

+ 0 - 734
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/utils/blobTool.js

@@ -1,734 +0,0 @@
-// import { GLOBAL } from '@/static/Global.js'
-import JsFn from '@/utils/js-fn.js'
-import Tools from '@/utils/public.js'
-import API from '@/api/index.js'
-// import store from '@/store'
-// import FileSaver from "file-saver";
-// import JSZip from "jszip";
-
-const BLOB_PATH = ['jointexam', 'avatar', 'audio', 'doc', 'exam', 'image', 'elegant', 'item', 'notice', 'other', 'paper', 'syllabus', 'res', 'records', 'student', 'survey', 'temp', 'thum', 'video', 'vote', 'jyzx', 'train', 'yxpt', 'homework', 'policy', 'public', 'art', 'activity', 'banner']
-const { BlobServiceClient } = require("@azure/storage-blob")
-
-//获取文件后缀和类型
-function getExAndType(fileName) {
-    let ex = fileName.substring(fileName.lastIndexOf('.') + 1)
-    let type = 'other'
-    ex = ex.toUpperCase()
-    for (let key in GLOBAL.CONTENT_TYPES) {
-        if (GLOBAL.CONTENT_TYPES[key].indexOf(ex) != -1) {
-            type = key
-            break
-        }
-    }
-    return {
-        ex, type
-    }
-}
-
-export default class BlobTool {
-    /**
-     * BlobTool生产方法
-     * @param {string} scope 学校(school)/个人(private)
-     * @param {object} options 初始化所需参数 host、container、sas
-     */
-    static CreateBlobTool(scope, options) {
-        if (!scope) {
-            throw new Error("CreateBlobTool参数(scope)错误,创建BlobTool失败")
-        }
-        //优先使用传入参数初始化
-        if (options) {
-            let { host, container, sas } = options
-            if (host && container && sas) {
-                return new BlobTool(host, container, '?' + sas, scope)
-            } else {
-                throw new Error("CreateBlobTool参数(options)异常,创建BlobTool失败")
-            }
-        }
-        let sas = scope == 'school' ? store.state?.user?.schoolProfile?.blob_sas : store.state?.user?.userProfile?.blob_sas
-        let blobUrl = scope == 'school' ? JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8"))?.blob_uri : JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8"))?.blob_uri
-        let host = blobUrl?.substring(0, blobUrl?.lastIndexOf('/'))
-        let cont = blobUrl?.substring(blobUrl?.lastIndexOf('/') + 1)
-        return new BlobTool(host, cont, '?' + sas, scope)
-    }
-    /**
-     * 公共读写容器 BlobTool生产方法
-     * @param {object} options 初始化所需参数 host、container
-     */
-    static CreatePublicBlobTool(options) {
-        if (!options) {
-            throw new Error("CreatePublicBlobTool参数(options)异常,创建BlobTool失败")
-        }
-        //优先使用传入参数初始化
-        let { host, container } = options
-        if (host && container) {
-            return new BlobTool(host, container, '', 'public')
-        } else {
-            throw new Error("CreatePublicBlobTool参数(options)异常,创建BlobTool失败")
-        }
-    }
-
-    /**
-     * 初始化Blob,需要先调用授权API
-     * @param {string} blobUrl blob地址
-     * @param {string} container 容器名称
-     * @param {string} sasString 授权 需要前面有 '?'
-     * @param {string} scope 学校(school)/个人(private) 计算空间大小
-     * */
-    constructor(blobUrl, container, sasString, scope) {
-        this.initBlob(blobUrl, container, sasString, scope)
-    }
-
-    /**
-     * 初始化Blob,需要先调用授权API
-     * @param {string} blobUrl blob地址
-     * @param {string} container 容器名称
-     * @param {string} sasString 授权
-     * @param {string} scope 学校(school)/个人(private) 计算空间大小
-     * */
-    initBlob(blobUrl, container, sasString, scope) {
-        if (blobUrl && container && scope) {
-            //初始化containerClient
-            this.blobService = new BlobServiceClient(blobUrl + sasString)
-            let containerClient = this.blobService.getContainerClient(container)
-            if (containerClient) {
-                this.containerClient = containerClient
-                this.container = container
-                this.blobUrl = blobUrl
-                this.sasString = sasString
-                this.scope = scope
-                if (scope == 'private') {
-                    let user_profile = localStorage.getItem('user_profile')
-                    this.blobSpace = user_profile ? JSON.parse(decodeURIComponent(user_profile, "utf-8")).total : 0
-                } else if (scope == 'school') {
-                    let school_profile_str = localStorage.getItem('school_profile') || '{}'
-                    let school_profile = JSON.parse(decodeURIComponent(school_profile_str, "utf-8"))
-                    this.blobSpace = school_profile && school_profile.school_base ? school_profile.school_base.size : 0
-                } else if(scope == 'area') {
-                    this.blobSpace = 0
-                }
-            } else {
-                throw new Error("initBlob参数错误,初始化失败")
-            }
-        } else {
-            throw new Error("initBlob初始化参数不完整,初始化失败")
-        }
-    }
-
-    /**
-     * 获取容器信息 (授权失败,需要账号级别的授权)
-     * @param {object} 
-     */
-    getProperties(options) {
-        return new Promise((r, j) => {
-            //const blockBlobClient = this.containerClient.getBlockBlobClient('res/基础操作范例_16x9.HTE') //blob获取成功
-            this.containerClient.getProperties(options).then(
-                res => {
-                    console.log('获取信息成功')
-                    r(res)
-                },
-                err => {
-                    console.log('获取信息失败')
-                    j(err)
-                }
-            )
-        })
-    }
-
-    /**
-     * 获取指定分块的md5值
-     * @param {any} url 相对路径 eg: video/test.mp4
-     */
-    getBlockMD5(url) {
-        let blobClient = this.containerClient.getBlockBlobClient(url)
-        return blobClient.download(0, 4 * 1024 * 1024 - 100, { rangeGetContentMD5: true })
-    }
-    /**
-     * 通过地址下载文件
-     * @param {any} url 相对路径 eg: video/test.mp4
-     */
-    downloadToFile(url) {
-        let blobClient = this.containerClient.getBlockBlobClient(url)
-        blobClient.download().then(
-            res => {
-                console.log('返回信息', res)
-                res.blobBody.then(
-                    blobRes => {
-                        console.log('blob结果', blobRes)
-                        let a = document.createElement("a")
-                        let url = window.URL.createObjectURL(blobRes)
-                        a.href = url
-                        a.download = 'test'
-                        a.click()
-                        window.URL.revokeObjectURL(url)
-                    },
-                    blobErr => {
-                        console.error('blob结果', blobErr)
-                    }
-                )
-            },
-            err => {
-                console.error('下载失败')
-            }
-        )
-    }
-
-    /**
-     * 上传文件方法,带回调上传进度
-     * @param {File} file 文件对象
-     * @param {Object} config 配置项 {path,root,checkExist,checkSize}
-     * {string} 必填 path 文件夹路径 只需要文件夹名称 前后都不需要加‘/’
-     * {string} 选填 root path验证的根目录 默认为'/' 
-     * {boolean} 选填 checkSize 上传时是否检查容器空间 默认为 true
-     * {boolean} 选填 checkExist 是否检查文件重复 默认为 false(直接覆盖) 如果为ture会重命名上传
-     * 
-     * @param {any} option 官方可配置项
-     * @returns {object} {url, name,size,createTime,extension,type}
-     */
-    // upload(file, path, option = {}, checkSize = true, root = '/') { //原来参数格式
-    upload(file, config = {}, option = {}) {
-        let { path, root, checkSize, checkExist } = config
-        console.log(config)
-        //验证和初始化参数
-        if (!path) throw new Error('上传失败:config.path必传')
-        root = root || '/'
-        checkSize = checkSize == undefined ? true : checkSize
-        checkExist = checkExist == undefined ? false : checkExist
-
-        let checkPath = root === '/' ? path : path.replace(root, '')
-        if (!BLOB_PATH.includes(checkPath.split('/')[0])) throw new Error('上传路径不合法,请检查上传路径:' + path)
-
-        return new Promise(async (r, j) => {
-            //检查容器空间大小
-            let isFull = false
-            if (checkSize) {
-                try {
-                    isFull = await this.isContainerFull(this.scope)
-                } catch (e) {
-                    j({ spaceError: '容器空间计算失败,无法上传文件' })
-                    return
-                }
-                if (isFull) {
-                    j({
-                        code: 1,
-                        spaceError: 'Blob空间已满,无法上传'
-                    })
-                    return
-                }
-            }
-
-            //检查文件是否存在
-            let fileName = file.name
-            let isExist = false
-            if (checkExist) {
-                let index = 1
-                while (await this.exists(path + '/' + fileName)) {
-                    fileName = `${file.name.slice(0, file.name.lastIndexOf('.'))}(${index})${file.name.slice(file.name.lastIndexOf('.'))}`
-                    isExist = true
-                    index++
-                }
-            }
-            const blockBlobClient = this.containerClient.getBlockBlobClient(path + "/" + fileName)
-            blockBlobClient.uploadBrowserData(file, option).then(
-                async res => {
-                    //设置blob MD5 (解决大文件分块上传没有MD5的问题)
-                    let md5value = res.contentMD5
-                    if (!md5value) {
-                        try {
-                            md5value = await Tools.getFileMD5(file)
-                            let option = {
-                                blobContentMD5: md5value
-                            }
-                            this.setFileProperties(path + "/" + fileName, option).then(
-                                setRes => {
-                                    console.log('MD5设置成功', setRes)
-                                },
-                                setErr => {
-                                    console.error('MD5设置失败', setErr)
-                                }
-                            )
-                        } catch (e) {
-                            console.error('前端获取MD5失败', e)
-                        }
-                    }
-                    let url = decodeURIComponent(res._response.request.url)
-                    url = url.substring(0, url.lastIndexOf('?'))
-                    let info = getExAndType(fileName)
-                    r({
-                        url: url,
-                        md5: md5value,
-                        blob: '/' + path + "/" + fileName,
-                        name: fileName,
-                        size: file.size,
-                        createTime: res.lastModified.getTime(),
-                        extension: info.ex,
-                        type: info.type,
-                        isExist: isExist
-                    })
-                },
-                err => {
-                    j(err)
-                }
-            )
-        })
-    }
-
-    /**
-     * 处理HTEX文件类型
-     * @param {blobList} fileList 
-     */
-    handleHTEXFile(fileList) {
-        let parseRes = []
-        let names = []
-        fileList.forEach((item, index) => {
-            if (item.url.indexOf('/res/') > 0 && item.url.indexOf('.HTE') < 0) {
-                let fileItem = {}
-                let startIndex = JsFn.findChartIndex(item.blob, '/', 1)
-                let endIndex = JsFn.findChartIndex(item.blob, '/', 2)
-                let name = item.blob.substring(startIndex + 1, endIndex)
-                let nameIndex = names.indexOf(name)
-                if (nameIndex == -1) {
-                    fileItem.url = this.blobUrl + '/' + this.container + '/res/' + name
-                    fileItem.blob = `/res/${name}/index.json`
-                    fileItem.name = name + '.HTEX'
-                    fileItem.size = item.size
-                    fileItem.createTime = item.createTime
-                    fileItem.extension = 'HTEX'
-                    fileItem.type = 'res'
-                    names.push(name)
-                    parseRes.push(fileItem)
-                } else {
-                    parseRes[nameIndex].size += item.size
-                }
-            } else {
-                parseRes.push(item)
-            }
-        })
-        return parseRes
-    }
-
-    /**
-     * 下载文件夹
-     * @param {string} folder 
-     * @param {number} options 
-     * {
-     *      fileName:'xxxx', 打包下载的文件名(必填)
-     *      exclude:'aaa', 在文件夹内但是不需要下载的内容
-     * }
-     * @returns 
-     */
-    downloadFolder(folder, options) {
-        const { fileName, exclude } = options
-        this.listBlob({
-            prefix: folder
-        }).then(
-            async res => {
-                const zip = new JSZip()
-                for (const blob of res.blobList) {
-                    try {
-                        if (exclude && blob.blob?.includes(exclude)) {
-                            continue
-                        }
-                        let blobPath = blob.blob.substring(1)
-                        let blobClient = this.containerClient.getBlockBlobClient(blobPath)
-                        const dwRes = await blobClient.download()
-                        const blobRes = await dwRes.blobBody
-                        const filePath = blobPath.replace(folder, '')
-                        zip.file(filePath, blobRes, {
-                            binary: true
-                        }) // 逐个添加文件
-                    } catch (e) {
-                        console.error('下载失败')
-                    }
-                }
-                zip.generateAsync({
-                    type: "blob"
-                }).then(content => {
-                    // 生成二进制流
-                    FileSaver.saveAs(content, fileName + ".zip"); // 利用file-saver保存文件
-                }).catch(err => {
-                    console.log(err);
-                })
-            }
-        )
-    }
-
-    /**
-     * 列出目录结构
-     * @param {string} delimiter  分割符 默认'/'
-     * @param {object} option 配置项 eg: option.prefix = 'res' 查某个目录的下级目录
-     * @returns 
-     */
-    listFolder(option, delimiter = '/') {
-        return new Promise(async (r, j) => {
-            let folderList = []
-            if (this.containerClient) {
-                let iter = this.containerClient.listBlobsByHierarchy(delimiter, option ? option : {})
-                try {
-                    let blobItem = await iter.next()
-                    while (!blobItem.done) {
-                        if (blobItem.value.kind === 'prefix') {
-                            folderList.push(blobItem.value.name)
-                        }
-                        blobItem = await iter.next()
-                    }
-                    r(folderList)
-                } catch (e) {
-                    console.error(e)
-                    j('获取目录结构异常')
-                }
-            } else {
-                j('初始化异常')
-            }
-        })
-    }
-
-    /**
-     * 列出所有(查询)
-     * @param {Object} option ContainerListBlobsOptions
-     * eg: option.prefix = 'res' 只查res文件夹下的blob
-     * @returns {object} {blobList, continuationToken}
-     */
-    listBlob(option, hendleHTEX = true) {
-        return new Promise(async (r, j) => {
-            let blobList = []
-            if (this.containerClient) {
-                let iter = this.containerClient.listBlobsFlat(option ? option : {});
-                let blobItem = await iter.next();
-                while (!blobItem.done) {
-                    let blobName = blobItem.value.name
-                    let info = getExAndType(blobItem.value.name)
-                    blobList.push(
-                        {
-                            url: this.blobUrl + '/' + this.container + '/' + blobName,
-                            blob: '/' + blobName,
-                            // name: blobName.substring(JsFn.findChartIndex(blobName, '/', 0) + 1),
-                            name: blobName.substring(blobName.lastIndexOf('/') + 1),
-                            size: blobItem.value.properties.contentLength,
-                            createTime: blobItem.value.properties.createdOn.getTime(),
-                            extension: info.ex,
-                            type: info.type
-                        }
-                    )
-                    blobItem = await iter.next();
-                }
-                if (hendleHTEX) {
-                    blobList = this.handleHTEXFile(blobList)
-                }
-                r({
-                    blobList,
-                    continuationToken: 'end'
-                })
-            } else {
-                j('containerClient 错误')
-            }
-        })
-    }
-
-    /**
-     * 分页列出(查询)
-     * @param {Object} option ContainerListBlobsOptions
-     * eg: option.prefix = 'res' 只查res文件夹下的blob
-     * @param {Object} pageInfo 
-     * eg: pageInfo.maxPageSize 当前请求条数
-     * eg: pageInfo.continuationToken 首次请求不需要,后面需要(首次请求会返回,下次需要传入)
-     * @returns {object} {blobList, continuationToken}
-     */
-    async listBlobByPage(option, pageInfo) {
-        return new Promise(async (r, j) => {
-            let page = {}
-            let blobList = []
-            if (pageInfo && JSON.stringify(pageInfo) != '{}') {
-                page.maxPageSize = pageInfo.maxPageSize ? pageInfo.maxPageSize : 50
-                if (pageInfo.continuationToken) {
-                    page.continuationToken = pageInfo.continuationToken ? pageInfo.continuationToken : ''
-                }
-            }
-            if (this.containerClient) {
-                let iterator = this.containerClient.listBlobsFlat(option ? option : {}).byPage(page)
-                let response = (await iterator.next()).value
-                let prefixLen = response.prefix ? response.prefix.length + 1 : 0
-                for (const blob of response.segment.blobItems) {
-                    let info = getExAndType(blob.name)
-                    blobList.push(
-                        {
-                            url: response.serviceEndpoint + response.containerName + '/' + blob.name,
-                            blob: '/' + blob.name,
-                            name: blob.name.substring(prefixLen),
-                            size: blob.properties.contentLength,
-                            createTime: blob.properties.lastModified.getTime(),
-                            extension: info.ex,
-                            type: info.type
-                        }
-                    )
-                }
-                let continuationToken = response.continuationToken ? response.continuationToken : 'end'
-                r({
-                    blobList,
-                    continuationToken
-                })
-            } else {
-                j('containerClient 错误')
-            }
-        })
-    }
-
-    /**
-     * 删除Blob
-     * @param {string} filePath 文件url + 容器 之后的路径
-     */
-    deleteBlob(filePath) {
-        if (filePath) {
-            filePath = filePath.substring(1)
-            return new Promise((r, j) => {
-                this.containerClient.deleteBlob(filePath).then(
-                    async res => {
-                        r(200)
-                    },
-                    err => {
-                        j(err)
-                    }
-                )
-
-            })
-        } else {
-            throw new Error("filePath参数错误")
-        }
-    }
-
-    /**
-     * 批量删除Blob 官方API授权失败(需要账号级别的授权,批量删除请访问后端API)
-     * @param {string} files 文件url + 容器 之后的路径
-     */
-    deleteBlobs(files) {
-        this.getProperties()
-        let blobBatchClient = this.blobService.getBlobBatchClient()
-        return new Promise((r, j) => {
-            for (let i in files) {
-                files[i] = files[i].substring(0, files[i].lastIndexOf('?'))
-            }
-            blobBatchClient.deleteBlobs(files, this.blobService.credential).then(
-                res => {
-                    console.log('批量删除成功')
-                },
-                err => {
-                    console.log('批量删除失败')
-                }
-            )
-        })
-    }
-
-    /**
-     * 批量删除Blob 循环操作
-     * @param {string} files 
-     */
-    deleteBlobBatch(files) {
-        return new Promise((r, j) => {
-            let promises = []
-            for (let item of files) {
-                let f = item.substring(1)
-                promises.push(this.containerClient.deleteBlob(f))
-            }
-            Promise.all(promises).then(
-                res => {
-                    r(res)
-                },
-                err => {
-                    j(err)
-                }
-            )
-        })
-    }
-
-    /**
-     * 获取blob属性
-     * @param {string} url //blob完整路径包含授权 eg:'doc/醍摩豆账号.xlsx'
-     */
-    getFileProperties(url) {
-        let blobClient = this.containerClient.getBlockBlobClient(url)
-        return blobClient.getProperties()
-    }
-
-    /**
-     * 设置文件header
-     * @param {string} url //blob完整路径包含授权 eg:'doc/醍摩豆账号.xlsx'
-     * @param {BlobHTTPHeaders} option //设置blob的属性
-     * eg:
-     * {
-     *      blobContentMD5: new Uint8Array(16)
-     * }
-     */
-    setFileProperties(url, option) {
-        let blobClient = this.containerClient.getBlockBlobClient(url)
-        return blobClient.setHTTPHeaders(option)
-    }
-
-    /**
-     * 复制单个Blob
-     * @param {string} targetUrl 
-     * @param {string} sourceUrl 
-     * @param {string} sas 
-     * 1、目标url(targetUrl)容器之后的文件路径 eg:'paper/預設試卷名稱00/gfWxV2p8lx07QGBSz5k401041200fIJd0E010.mp4'
-     * 2、源文件url(sourceUrl)1、完整路径(包括Host); 2、如果需要授权的容器,则url需要凭借授权。
-     */
-    copyBlob(targetUrl, sourceUrl, sas) {
-        return new Promise((r, j) => {
-            console.log(...arguments)
-            let newBlob = this.containerClient.getBlobClient(targetUrl)
-            let encodeUrl = encodeURI(sourceUrl)
-            newBlob.beginCopyFromURL(encodeUrl + sas).then(
-                res => {
-                    console.log('复制成功返回数据', res)
-                    r(200)
-                },
-                err => {
-                    console.log('11111111111', err);
-                    j(500)
-                }
-            )
-        })
-    }
-
-    /**
-     * 复制‘文件夹’
-     * @param {string} targetFolder eg:'exam/评测id/paper/8b94c6b6-2572-41e5-89b9-a82fcf13891e/' 注意开头不加‘/’, 结尾需要加‘/’
-     * @param {string} sourceFolder eg:'paper/JEFF組卷測試01'
-     * @param {BlobTool} blobTool 非必传参数, 当目标文件和源文件不在同一个容器的时候,需要传源文件容器初始化的BlobTool
-     * @param {boolean} handleSize 是否内部处理blobsize
-     */
-    copyFolder(targetFolder, sourceFolder, blobTool, handleSize = true) {
-        console.log(...arguments);
-        return new Promise(async (r, j) => {
-            try {
-                let blobs = undefined
-                let sasString = ''
-                if (blobTool) {
-                    blobs = await blobTool.listBlob({
-                        prefix: sourceFolder + '/'
-                    }, false)
-                    sasString = blobTool.sasString
-                } else {
-                    blobs = await this.listBlob({
-                        prefix: sourceFolder + '/'
-                    }, false)
-                    sasString = this.sasString
-                }
-                //上传之前检查文件夹大小
-                let beforeSize = 0
-                let cont = ''
-                if (handleSize) {
-                    cont = this.container
-                }
-                if (blobs && blobs.blobList.length) {
-                    let count = 0
-                    blobs.blobList.forEach(blobItem => {
-                        let newUrl = targetFolder + blobItem.name
-                        let newBlob = this.containerClient.getBlobClient(newUrl)
-                        let resourceUrl = encodeURI(blobItem.url)
-                        newBlob.beginCopyFromURL(resourceUrl + sasString).then(
-                            async res => {
-                                if (++count == (blobs.blobList.length)) {
-                                    if (handleSize) {
-                                        //复制之后更新大小
-                                    }
-                                    r(blobItem)
-                                }
-                            },
-                            err => {
-                                j('copy error')
-                            }
-                        )
-                    })
-                } else {
-                    j('sourceBlob error or 404 : ' + sourceFolder)
-                }
-            } catch {
-                j('copy error')
-            }
-
-        })
-    }
-
-    /**
-     * 判断文件是否存在
-     * @param {string} filePath 文件路径 正确 'res/基础操作范例_16x9.HTE ' 错误 '/res/基础操作范例_16x9.HTE'
-     * @param {object} 
-     * return true/false
-     */
-    exists(filePath, options) {
-        const blockBlobClient = this.containerClient.getBlockBlobClient(filePath)
-        return new Promise((r, j) => {
-            blockBlobClient.exists(options).then(
-                res => {
-                    r(res)
-                },
-                err => {
-                    j(err)
-                }
-            )
-        })
-    }
-
-    /**
-     * 判断容器的空间是否已满
-     * @params {containerName} 容器名称
-     * @params {scope} 'school' or 'private'
-     * return true/false
-     */
-    async isContainerFull(scope) {
-        return new Promise(async (r, j) => {
-            try {
-                let sizeRes = await BlobTool.getContainerSize(this.container, scope)
-                console.log(scope)
-                console.log(sizeRes)
-                if (sizeRes) {
-                    r(sizeRes.total > this.blobSpace * 1024 * 1024 * 1024)
-                } else {
-                    j('容器空间判断失败!')
-                }
-            } catch (e) {
-                j('容器空间判断失败!')
-            }
-
-        })
-    }
-
-    /**
-     * 新版获取blob空间大小
-     * @params {containerName} 容器名称
-     * @params {scope} 'school' or 'private'
-     */
-    static getContainerSize(containerName, scope) {
-        if (containerName, scope) {
-            return new Promise((r, j) => {
-                API.blob.getContainerSize({
-                    scope,
-                    containerName
-                }).then(
-                    res => {
-                        if (res) {
-                            let contentSize = 0
-                            let defined = ['image', 'res', 'video', 'audio', 'doc', 'other']
-                            for (let key in res.catalog) {
-                                if (defined.includes(key)) {
-                                    contentSize += res.catalog[key]
-                                }
-                            }
-                            res.catalog.appData = res.size - contentSize
-                            res.catalog.total = res.size
-                            res.catalog.teachSpace = res.teach ? res.teach * 1024 * 1024 * 1024 : 0 //查询学校空间的时候,返回此学校已分配给教室的空间;查询个人容器返回为零,不需要处理
-                            r(res.catalog)
-                        } else {
-                            j('API error')
-                        }
-                    },
-                    err => {
-                        j('API error')
-                    }
-                )
-            })
-        } else {
-            throw new Error("参数不完整")
-        }
-
-    }
-}

+ 0 - 490
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/utils/js-fn.js

@@ -1,490 +0,0 @@
-
-// import store from '@/store'
-import { app } from '@/main.js'
-import _tools from "./public.js"
-// import _editorTools from "./editorTools.js"
-/*
- * 根据某个属性进行分组
- */
-function groupBy(array, key) {
-    const groups = {}
-    array.forEach(function (item) {
-        const group = JSON.stringify(item[key])
-        groups[group] = groups[group] || []
-        groups[group].push(item)
-    })
-    return Object.keys(groups).map(function (group) {
-        return groups[group]
-    })
-}
-/*
- * 判断两个对象是否相等
- */
-function isObjEqual(o1, o2) {
-    var props1 = Object.keys(o1)
-    var props2 = Object.keys(o2)
-    if (props1.length != props2.length) {
-        return false
-    }
-    for (var i = 0, max = props1.length; i < max; i++) {
-        var propName = props1[i]
-        if (o1[propName] !== o2[propName]) {
-            return false
-        }
-    }
-    return true
-}
-/*
- * 获取对象在对象数组的index
- */
-function getIndex(_arr, _obj) {
-    var len = _arr.length
-    for (let i = 0; i < len; i++) {
-        if (isObjEqual(_arr[i], _obj)) {
-            return parseInt(i)
-        }
-    }
-    return -1
-}
-/*
- * 产生某个范围的随机数
- */
-function getBtwRandom(start, end) {
-    return Math.floor(Math.random() * (end - start)) + start
-}
-
-/*
- *根据图片链接压缩图片得到缩略图 
- * @params url: 文件url
- */
-function compressImgByUrl(url, name, quality) {
-    return new Promise((r, j) => {
-        try {
-            let img = new Image()
-            img.setAttribute('crossOrigin', 'Anonymous')
-            img.src = url
-            img.onload = function () {
-                let canvas = document.createElement('canvas')
-                canvas.width = 200
-                canvas.height = 200 * img.height / img.width
-                let ctx = canvas.getContext('2d')
-                ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height)
-                let newImgData = canvas.toDataURL('image/png', quality)
-                //let resultFile = dataURLtoFile(newImgData, name)
-                //r(resultFile)
-                r(newImgData)
-            }
-            img.onerror = function (e) {
-                console.error('图片Error', e)
-                j('Format Error')
-            }
-        }
-        catch (err) {
-            j(err)
-        }
-    })
-}
-/*
- *根据视频链接创建封面图
- * @params url: 文件url
- */
-function createVideoPoster(url, name, quality) {
-    return new Promise(
-        (r, j) => {
-            try {
-                let video = document.createElement('video')
-                video.setAttribute('crossOrigin', 'Anonymous')
-                video.setAttribute('width', '300')
-                video.setAttribute('controls', 'controls')
-                video.setAttribute('crossOrigin', 'Anonymous')
-                video.setAttribute('src', url)
-                video.currentTime = 1
-                console.log(url)
-                video.addEventListener('loadeddata', () => {
-                    let canvas = document.createElement('canvas')
-                    canvas.width = 300
-                    canvas.height = 300 * video.videoHeight / video.videoWidth
-                    let ctx = canvas.getContext('2d')
-                    ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, canvas.width, canvas.height)
-                    let newVideoData = canvas.toDataURL('image/png', quality)
-                    //let resultFile = dataURLtoFile(newVideoData, name)
-                    r(newVideoData)
-                })
-                video.addEventListener('error', (e) => {
-                    console.error(e)
-                    j('Format Error')
-                })
-            }
-            catch (err) {
-                j(err)
-            }
-        }
-    )
-}
-
-/*
- *dataUrl转文件 
- */
-function dataURLtoFile(dataurl, filename) {
-    console.log(...arguments);
-    let arr = dataurl.split(',')
-    let mime = arr[0].match(/:(.*?);/)[1]
-    let bstr = atob(arr[1])
-    let n = bstr.length
-    let u8arr = new Uint8Array(n)
-    while (n--) {
-        u8arr[n] = bstr.charCodeAt(n)
-    }
-    return new File([u8arr], filename, { type: mime })
-}
-/*
- *文件转dataUrl
- */
-function fileToURL(file) {
-    return new Promise((r, j) => {
-        try {
-            var reader = new FileReader()
-            reader.onloadend = function (e) {
-                r(e.target.result)
-            }
-            reader.readAsDataURL(file)
-        } catch (e) {
-            j(e)
-        }
-    })
-
-}
-
-/*
- * 函数防抖
- */
-function debounce(func, delay) {
-    return function () {
-        window.clearTimeout(window.timeout)
-        window.timeout = setTimeout(() => {
-            clearTimeout(window.timeout)
-            func.apply(this, arguments)
-        }, delay)
-    }
-}
-
-/*
- * 函数节流
- */
-function throttle(func, delay) {
-    let run = true
-    return function () {
-        if (!run) {
-            return  // 如果开关关闭了,那就直接不执行下边的代码
-        }
-        run = false // 持续触发的话,run一直是false,就会停在上边的判断那里
-        setTimeout(() => {
-            func.apply(this, arguments)
-            run = true // 定时器到时间之后,会把开关打开,我们的函数就会被执行
-        }, delay)
-    }
-}
-//转换bytes
-function formatBytes(bytes) {
-    bytes = bytes || 0
-    return bytes / 1024 < 1024 ? (bytes / 1024).toFixed(1) + 'KB' : bytes / 1024 / 1024 < 1024 ? (bytes / 1024 / 1024).toFixed(1) + 'M' : (bytes / 1024 / 1024 / 1024).toFixed(1) + 'G'
-}
-//生成uuid
-function uuid() {
-    function S4() {
-        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
-    }
-    return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
-}
-// 获取某个字符在字符串中第num次出现的index
-function findChartIndex(str, cha, num) {
-    var x = str.indexOf(cha);
-    for (var i = 0; i < num; i++) {
-        x = str.indexOf(cha, x + 1);
-    }
-    return x;
-}
-
-function secondTimeFormat(timestamp) {
-    timestamp = timestamp < 10000000000 ? timestamp * 1000 : timestamp
-    let date = new Date(timestamp)
-    let Y = date.getFullYear()
-    let M = date.getMonth()
-    let D = date.getDate()
-    let H = date.getHours()
-    let MIN = date.getMinutes()
-    let SEC = date.getSeconds()
-    return `${Y}/${M < 9 ? '0' + (M + 1) : M + 1}/${D < 10 ? '0' + D : D} ${H < 10 ? '0' + H : H}:${MIN < 10 ? '0' + MIN : MIN}:${SEC < 10 ? '0' + SEC : SEC}`
-}
-
-function timeFormat(timestamp) {
-    timestamp = timestamp < 10000000000 ? timestamp * 1000 : timestamp
-    let date = new Date(timestamp)
-    let Y = date.getFullYear()
-    let M = date.getMonth()
-    let D = date.getDate()
-    let H = date.getHours()
-    let MIN = date.getMinutes()
-    return `${Y}/${M < 9 ? '0' + (M + 1) : M + 1}/${D < 9 ? '0' + D : D} ${H < 10 ? '0' + H : H}:${MIN < 10 ? '0' + MIN : MIN}`
-}
-//時間戳轉換
-function dateFormat(timestamp) {
-    if (timestamp <= 0) return ''
-    timestamp = timestamp < 10000000000 ? timestamp * 1000 : timestamp
-    let date = new Date(timestamp)
-    let Y = date.getFullYear()
-    let M = date.getMonth()
-    let D = date.getDate()
-    return `${Y}/${M < 9 ? '0' + (M + 1) : M + 1}/${D < 9 ? '0' + D : D}`
-}
-
-/**
- * 根据学年获取年级名称
- * @param year 学年
- */
-function getGradeNameByYear(year) {
-    if (year && year > 0) {
-        let curPeriod = store.state.user?.curPeriod
-        if (curPeriod) {
-            let date = new Date()
-            let curYear = date.getFullYear()
-            let month = date.getMonth() + 1
-            let start = curPeriod.semesters.find(item => {
-                return item.start == 1
-            })
-            // 根据入学月份确定当前年级和学级的关系
-            if (start && month < start.month) {
-                curYear--
-            }
-            let res = curPeriod.grades[curYear - year]
-            if (curYear - year >= curPeriod.grades.length) {
-                return app.$t('schoolBaseInfo.graduated')
-            }
-            return res ? res : app.$t('schoolBaseInfo.untimed')
-        } else {
-            return '--'
-        }
-    } else {
-        return '--'
-    }
-}
-
-/**
- * 根据学年获取年级信息
- * @param year 学年
- * @param periodId 学段id(非必填)
- */
-function getGradeInfoByYear(year, periodId) {
-    if (year && year > 0) {
-        let curPeriod
-        if (periodId) {
-            curPeriod = store.state.user?.schoolProfile?.school_base?.period?.find(item => item.id == periodId)
-        } else {
-            curPeriod = store.state.user?.curPeriod
-        }
-        if (curPeriod) {
-            let date = new Date()
-            let curYear = date.getFullYear()
-            let month = date.getMonth() + 1
-            let start = curPeriod.semesters.find(item => {
-                return item.start == 1
-            })
-            // 根据入学月份确定当前年级和学级的关系
-            if (start && month < start.month) {
-                curYear--
-            }
-            let res = curPeriod.grades[curYear - year]
-            let gradeName
-            if (curYear - year >= curPeriod.grades.length) {
-                gradeName = app.$t('schoolBaseInfo.graduated')
-            }
-            gradeName = res ? res : app.$t('schoolBaseInfo.untimed')
-            return {
-                id: curYear - year,
-                name: gradeName
-            }
-        } else {
-            return undefined
-        }
-    } else {
-        return undefined
-    }
-}
-
-/**
- * 根据班级学年年级名称
- * @param data 学校基础数据 schoolProfile.school_base
- * @param curPd 当前学段id
- * @param grade 年级index
- */
-function getYearByGrade(grade) {
-    let date = new Date()
-    let curYear = date.getFullYear()
-    let month = date.getMonth() + 1
-    grade = parseInt(grade)
-    let curPeriod = store.state.user?.curPeriod
-    if (grade > -1 && curPeriod) {
-        let start = curPeriod.semesters.find(item => {
-            return item.start == 1
-        })
-        // 根据入学月份确定当前年级和学级的关系
-        if (start && month < start.month) {
-            curYear--
-        }
-        return curYear - grade
-    } else {
-        return curYear
-    }
-}
-/**
- * 根据教师绑定的学科id获取学科和学段名称
- * @param ids 学科ids
- * @returns 
- * [
- *  {
- *      periodId:'ddd'
- *      periodName:'小学',
- *      subjectId:'wwww',
- *      subjectName:'语文'
- *  }
- * ]
- */
-function getTeacherSubjects(ids) {
-    let schoolPeriod = store.state.user?.schoolProfile?.school_base?.period
-    if (ids && ids.length && schoolPeriod) {
-        let data = []
-        ids.forEach(sid => {
-            for (let i = 0; i < schoolPeriod.length; i++) {
-                let subjectInfo = schoolPeriod[i].subjects.find(subject => subject.id === sid)
-                if (subjectInfo) {
-                    data.push({
-                        periodId: schoolPeriod[i].id,
-                        periodName: schoolPeriod[i].name,
-                        subjectId: subjectInfo.id,
-                        subjectName: subjectInfo.name
-                    })
-                    break
-                }
-            }
-        })
-        return data
-    } else {
-        return []
-    }
-
-}
-//根据域名判断菜单是否显示
-function checkJinNiu() {
-    let host = window.location.host
-    return host == 'jinniu.teammodel.cn'
-}
-//根据域名判断菜单是否显示 知音
-function checkZhiYin() {
-    let host = window.location.host
-    // return host == 'zhiyin.teammodel.cn'
-    return host=='zhiyin-test.teammodel.cn'
-}
-//根据域名判断菜单是否显示
-function checkTrain() {
-    let host = window.location.host
-    return host == 'scyx.teammodel.cn'
-}
-/**
- * 统一处理学生作答数据
- * @param {string} fullUrl ans.json完成路径含授权
- * @param {string} richPrefix 富文本前缀(到stuId)
- * @param {string} sas 授权(不需要“?”)
- */
-function handleStudentAnswer(fullUrl, richPrefix, sas) {
-    return new Promise(async (r, j) => {
-        try {
-            let ansStr = await _tools.getFile(fullUrl)
-            if (ansStr) {
-                let ans = JSON.parse(ansStr)
-                ans.forEach((item, index) => {
-                    // 数据异常
-                    if (!item) {
-                        ans[index] = [app.$t('learnActivity.score.ansErr')]
-                    }
-                    // 未作答
-                    // 2024.4.7 题型增加问答题,保存字段为blob地址,需判断是否作答,因此返回[]
-                    else if (!item.length) {
-                        // ans[index] = [app.$t('learnActivity.score.noStuAns')]
-                        ans[index] = []
-                    }
-                    // 处理富文本中多媒体
-                    else {
-                        item.forEach((as, ai) => {
-                            item[ai] = _editorTools.getMideaFullPath(as, richPrefix + '/', '?' + sas)
-                        })
-                        ans[index] = item
-                    }
-                })
-                r(ans)
-            } else {
-                r([])
-            }
-        } catch (e) {
-            r([])
-        }
-    })
-}
-function getBlobInfo(scope) {
-    console.log(store)
-    let blobProfile
-    if (scope == 'school') {
-        blobProfile = store.state.user.schoolProfile
-    } else if (scope == 'private') {
-        blobProfile = store.state.user.userProfile
-    }
-    if (blobProfile) {
-        let blobUrl = blobProfile.blob_uri
-        let blobName = blobUrl.substring(blobUrl.lastIndexOf('/') + 1)
-        let blobHost = blobUrl.substring(0, blobUrl.lastIndexOf('/'))
-        let blobSas = blobProfile.blob_sas
-        return {
-            blobUrl, blobName, blobHost, blobSas
-        }
-    } else {
-        return {}
-    }
-}
-
-function setLocalLang() {
-    // 自动根据浏览器系统语言设置语言(优先判断本地设置的语言,如果有则使用本地设置的语言,如果没有则使用浏览器系统语言)
-    const curLang = localStorage.getItem('cloudSetting') ? JSON.parse(localStorage.getItem('cloudSetting')).curLang : null
-    const navLang = curLang || navigator.language.toLowerCase()
-    const localLang = (navLang === 'zh' || navLang === 'zh-tw' || navLang === 'zh-cn' || navLang === 'zh-hk') ? navLang : false
-    let lang = localLang || 'en-us'
-    localStorage.setItem('local', lang)
-    return lang
-}
-
-export default {
-    groupBy,
-    isObjEqual,
-    getIndex,
-    getBtwRandom,
-    compressImgByUrl,
-    createVideoPoster,
-    dataURLtoFile,
-    fileToURL,
-    debounce,
-    throttle,
-    formatBytes,
-    uuid,
-    findChartIndex,
-    getGradeNameByYear,
-    getGradeInfoByYear,
-    getYearByGrade,
-    dateFormat,
-    timeFormat,
-    secondTimeFormat,
-    getTeacherSubjects,
-    checkJinNiu,
-    checkZhiYin,
-    checkTrain,
-    handleStudentAnswer,
-    getBlobInfo,
-    setLocalLang
-}

File diff suppressed because it is too large
+ 0 - 2249
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/utils/public.js


+ 0 - 68
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/utils/signalR.js

@@ -1,68 +0,0 @@
-import * as signalR from '@microsoft/signalr'
-
-export default class SignalRService {
-    // option 为调用 SignalRService 类传进来的参数
-    constructor(option) {
-        // 初始化 SignalR 连接对象为 null
-        this.connection = null
-        // 初始化重试次数为 0
-        this.retryCount = 0
-        // 最大重试次数
-        this.maxRetryAttempts = 5
-        // 重试间隔(毫秒)
-        this.retryInterval = 5000
-    }
-
-    // 初始化 SignalR 连接的方法
-    initSignalR(url) {
-        // 创建 SignalR 连接对象,并指定后端的 url
-        this.connection = new signalR.HubConnectionBuilder().withUrl(url).configureLogging(signalR.LogLevel.Information).build()
-
-        // 调用启动连接的方法
-        this.startConnection()
-    }
-
-    // 实际启动连接的方法
-    startConnection() {
-        // 尝试启动连接
-        this.connection.start().then(() => {
-            // 连接成功,打印日志并重置重试次数
-            console.log('Connected to SignalR successfully!');
-            this.retryCount = 0
-        }).catch(err => {
-            // 连接失败,打印错误日志
-            console.error('Error connecting to SignalR:', err);
-            // 如果重试次数 < 最大重试次数
-            if(this.retryCount < this.maxRetryAttempts) {
-                // 设置一个定时器,在指定间隔后重试连接,并增加重试次数
-                setTimeout(() => {
-                    this.retryCount++
-                    this.startConnection()
-                }, this.retryInterval)
-            }
-        })
-    }
-
-    // 用于注册消息接收回调函数的方法
-    onMessageReceived(callback) {
-        // .net 在连接对象上注册消息接收事件的回调函数
-        this.connection.on("ReceiveMessage", callback)
-
-        // java 在连接对象上注册消息接收事件的回调函数
-        // this.connection.on('message', callback)
-    }
-
-    onReceiveConnection(callback) {
-        this.connection.on('ReceiveConnection', callback)
-    }
-
-    // 停止 SignalR 连接的方法
-    stopSignalR() {
-        // 如果存在连接对象
-        if(this.connection) {
-            // 停止连接,并打印日志
-            this.connection.stop()
-            console.log('Disconnected from SignalR.');
-        }
-    }
-}

+ 0 - 141
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/admin/ActivityManage.less

@@ -1,141 +0,0 @@
-.el-container {
-    height: 100%;
-
-    .el-header,
-    .el-footer {
-        background-color: #b3c0d1;
-        color: #333;
-        line-height: 60px;
-    }
-
-    .el-aside {
-        // background-color: #d3dce6;
-        color: #333;
-        line-height: 200px;
-        border-right: 1px solid #ccc;
-    }
-
-    .el-main {
-        color: #333;
-        height: 100%;
-
-        .name-box {
-            font-size: 20px;
-            display: flex;
-            margin-bottom: 20px;
-
-            &>span {
-                font-weight: bold;
-            }
-
-            .paper-item-school {
-                display: flex;
-                margin-bottom: 5px;
-                font-size: 17px;
-                margin-right: 15px;
-                cursor: pointer;
-
-                &>span {
-                    padding: 2px 10px;
-                    // white-space: pre;
-                    display: flex;
-                    align-items: center;
-                }
-
-                .paper-owner {
-                    background-color: #ababab;
-                    color: #fff;
-                    border-radius: 5px 0 0 5px;
-                }
-
-                .paper-extType {
-                    border: 1px solid #ababab;
-                    border-radius: 0 5px 5px 0;
-                    border-left: none;
-                }
-            }
-
-            .short-code {
-                position: absolute;
-                right: 0;
-                margin-right: 20px;
-                // font-weight: bold;
-                display: flex;
-
-                span {
-                    font-weight: bold;
-                }
-
-                .el-button {
-                    margin-left: 15px;
-                }
-            }
-        }
-
-        .activity-info {
-            margin-top: 15px;
-            font-size: 16px;
-            height: 100%;
-
-            &>div {
-                margin-top: 15px;
-            }
-
-            .time-tag {
-                color: #4093E9;
-                font-weight: bolder;
-            }
-
-            .subject-tag {
-                background: #4093e9;
-                color: #fff;
-                border-radius: 5px;
-                padding: 5px 15px;
-                margin-right: 15px;
-            }
-
-            .group-tag {
-                background: #e6eff8;
-                // color: #fff;
-                border-radius: 5px;
-                padding: 5px 15px;
-                margin-right: 15px;
-                margin-bottom: 10px;
-                display: inline-block;
-            }
-
-            .paper-content {
-                background: #f7f7f7;
-                padding: 20px;
-                margin: 10px 20px 10px 0;
-                border-radius: 6px;
-
-                &>div {
-                    margin-bottom: 20px;
-                }
-
-                .subject-name {
-                    font-weight: 600;
-                    color: #2d8cf0;
-                    margin-right: 10px;
-                    margin-bottom: 15px;
-                }
-
-                .el-tag {
-                    margin-bottom: 10px;
-                    margin-right: 15px;
-                    cursor: pointer;
-                }
-            }
-        }
-    }
-}
-
-.el-container:nth-child(5) .el-aside,
-.el-container:nth-child(6) .el-aside {
-    line-height: 260px;
-}
-
-.el-container:nth-child(7) .el-aside {
-    line-height: 320px;
-}

File diff suppressed because it is too large
+ 0 - 317
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/admin/ActivityManage.vue


+ 0 - 526
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/login/Admin.vue

@@ -1,526 +0,0 @@
-<template>
-    <div class="login">
-        <div class="login-body">
-            <div class="login-box">
-                <div class="body-left">
-                    <div v-if="deviceInfo && deviceInfo.server.school" class="bind-school">
-                        <div style="margin-bottom: 20px;">
-                            <span style="border-right: 1px solid #b3b3b3; margin-right: 15px; padding-right: 15px;">{{ deviceInfo.server.school.name }}</span>
-                            <span>{{ deviceInfo.server.school.id }}</span>
-                        </div>
-                        <img :src="deviceInfo.server.school.picture" alt="">
-                    </div>
-                    <div v-else class="no-bind-school">未绑定学校</div>
-                </div>
-                <div class="body-right">
-                    <div class="right-input" v-show="!isQRCode">
-                        <h1>手机号登录</h1>
-                        <el-input v-model="loginForm.phone" placeholder="手机号码" size="medium" />
-                        <div>
-                            <el-input v-model="loginForm.smspin" placeholder="密码" size="medium" style="width: 60%;" />
-                            <el-button type="primary" size="medium" @click="getCode('smspin')" style="width: calc(40% - 20px); margin-left: 20px;">获取验证码</el-button>
-                        </div>
-                        <el-button type="primary" size="medium" @click="showPrivacy('smspin')">登录</el-button>
-                    </div>
-                    <div class="right-input" v-show="isQRCode">
-                        <h1 style="margin-bottom: 20px;">HiTA扫码登录</h1>
-                        <div style="text-align: center;">
-                            <img :src="qrCodeImg.qrcode" alt="" style="width: 230px;">
-                        </div>
-                        <el-button type="primary" size="medium" v-show="qrCodeToken" @click="showPrivacy('qrcode')">扫码成功,请登录</el-button>
-                    </div>
-                    <!-- 未联网时使用 -->
-                    <div style="display: flex; justify-content: space-between;">
-                        <span @click="showPrivacy('visitor')" style="cursor: pointer;">访客登录</span>
-                        <div class="switch-school" @click="isBindSchool = true">
-                            <i class="el-icon-qiehuan"></i>
-                            切换学校
-                        </div>
-                    </div>
-                    <div class="network-info">
-                        <span @click="showDevice()">
-                            <i v-show="hybridType" class="el-icon-success" style="color: #0eb90e;"></i>
-                            <i v-show="!hybridType" class="el-icon-error" style="color: #fb3636;"></i>
-                            服务端信息
-                        </span>
-                    </div>
-                </div>
-                <div class="qr-code" @click="changeLoginType()">
-                    <img src="@/assets/qrCode.png" alt="">
-                </div>
-            </div>
-        </div>
-        <div class="login-footer">
-            <a class="footer-info-item">蜀ICP备18027363号</a>
-            <span class="footer-info-item">© 2021 HABOOK Group 醍摩豆</span>
-        </div>
-        <el-drawer title="服务端信息" :visible.sync="isDeviceDrawer">
-            <el-form ref="form" label-width="120px" v-if="deviceInfo" style="margin-right: 10px;">
-                <el-form-item label="登录用户:">
-                    <span>{{ deviceInfo.server.userName }}</span>
-                </el-form-item>
-                <el-form-item label="设备名称:">
-                    <span>{{ deviceInfo.server.name }}</span>
-                </el-form-item>
-                <el-form-item label="操作系统:">
-                    <span>{{ deviceInfo.server.os }}</span>
-                </el-form-item>
-                <el-form-item label="系统类型:">
-                    <span>{{ deviceInfo.server.bit }}位操作系统 基于{{ deviceInfo.server.arch }}的处理器</span>
-                </el-form-item>
-                <el-form-item label="设备编号:">
-                    <span>{{ deviceInfo.server.deviceId }}</span>
-                </el-form-item>
-                <el-form-item label="网络地址:">
-                    <span>{{ deviceInfo.ip }}</span>
-                </el-form-item>
-                <el-form-item label="内存大小:">
-                    <span>{{ deviceInfo.server.shwoRam }}GB</span>
-                </el-form-item>
-                <el-form-item label="处理器:">
-                    <span v-for="(item, index) in deviceInfo.server.cpuInfos" :key="index" style="display: block;">{{ item.name }} {{ item.showHZ }}Hz({{ deviceInfo.server.cpu }}核)</span>
-                </el-form-item>
-                <el-form-item label="网卡信息:">
-                    <span v-for="(item, index) in deviceInfo.server.networks" :key="index" style="display: block;">{{ item.name }}({{ item.mac }}) </span>
-                </el-form-item>
-                <el-form-item label="数据中心:">
-                    <span>{{ deviceInfo.centerUrl }}</span>
-                </el-form-item>
-                <el-form-item label="服务端地址:">
-                    <span v-for="(item, index) in deviceInfo.server.host" :key="index" style="display: block;">
-                        {{ item }}
-                        <i class="el-icon-copy-document" @click="copyUrl(item)" style="cursor: pointer; margin-left: 5px;"></i>
-                    </span>
-                </el-form-item>
-            </el-form>
-        </el-drawer>
-        <el-dialog title="用户协议与隐私政策" width="30%" :visible.sync="isPrivacy">
-            <p>
-                为了更好地保障您的个人权益,请仔细阅读
-                <a href="/description" target="_blank">《用户协议》</a>与
-                <a href="/privacy" target="_blank">《隐私政策》</a>
-                内的所有条款。如您同意以上协议内容,请点击“同意并继续”。
-            </p>
-            <span slot="footer" class="dialog-footer">
-                <el-button @click="isPrivacy = false">不同意</el-button>
-                <el-button type="primary" @click="toLogin(loginType)">同意并继续</el-button>
-            </span>
-        </el-dialog>
-        <el-dialog title="请绑定学校" width="30%" :visible.sync="isBindSchool" :show-close="bindSchoolType" :close-on-press-escape="bindSchoolType" :close-on-click-modal="bindSchoolType">
-            <el-form ref="form" :model="schoolInfo" label-width="80px">
-                <el-form-item label="学校名称">
-                    <el-select size="medium" filterable v-model="schoolInfo.name" placeholder="请选择学校" style="width: 100%;">
-                        <el-option v-for="item in schoolList" :key="item.id" :label="item.name" :value="item.name"></el-option>
-                    </el-select>
-                </el-form-item>
-                <el-form-item label="学校简码">
-                    <el-input size="medium" v-model="schoolInfo.id"></el-input>
-                </el-form-item>
-            </el-form>
-            <span slot="footer" class="dialog-footer">
-                <el-button type="primary" @click="bindSchool()">绑定学校</el-button>
-            </span>
-        </el-dialog>
-    </div>
-</template>
-
-<script>
-import SignalRService from '@/utils/signalR';
-
-export default {
-    data() {
-        return {
-            loginForm: {
-                phone: '',
-                smspin: '',
-            },
-            isQRCode: false,
-            hybridType: 0, //是否连接云端服务器
-            isDeviceDrawer: false,
-            deviceInfo: undefined,
-            qrCodeImg: {qrcode: ''},
-            isBindSchool: false,
-            schoolInfo: {
-                id: '',
-                name: '',
-            },
-            isPrivacy: false,
-            schoolList: [],
-            loginType: '',
-            signalR: null,
-            qrCodeToken: undefined,
-        }
-    },
-    mounted() {
-        this.getSchoolList()
-        this.viewNetworkInfo()
-    },
-    methods: {
-        async viewNetworkInfo() {
-            let params = {
-                fp: await this.$tools.getFingerprint()
-            }
-            this.$api.getDevice(params).then(res => {
-                res.data.server.host = []
-                res.data.server.uris.forEach(item => {
-                    res.data.server.networks.forEach(network => {
-                        // https://192.168.8.140:5001/login/student
-                        let url = `${item.protocol}://${network.ip}:${item.port}/login/student`
-                        res.data.server.host.push(url)
-                    })
-                })
-                res.data.server.shwoRam = (res.data.server.ram / 1024 / 1024 / 1024).toFixed(1)
-                res.data.server.cpuInfos.forEach(item => {
-                    item.showHZ = item.hz ? (item.hz / 1000) : 0
-                });
-                if(!res.data.server.school) this.isBindSchool = true
-                this.deviceInfo = res.data
-                this.hybridType = res.data?.hybrid
-            })
-        },
-        showDevice() {
-            // 打开弹窗,显示详细信息
-            this.isDeviceDrawer = true
-        },
-        copyUrl(url) {
-            navigator.clipboard.writeText(url).then(() => {
-                this.$message({
-                    message: '文本已复制到剪切板',
-                    type: 'success'
-                });
-            }).catch(err => {
-                console.error('无法将文本复制到剪切板:', err);
-            });
-        },
-        getCode(type) {
-            let params = {
-                type: type,
-            }
-            if(type === 'smspin') {
-                params.area = '86'
-                params.to = this.loginForm.phone
-            }
-            this.$api.getCode(params).then(res => {
-                if(res.code === 200) {
-                    if(type === 'smspin' && res.send === 1) {
-                        this.$message({
-                            message: '验证码已发送',
-                            type: 'success'
-                        });
-                    } else {
-                        this.qrCodeImg = res
-                        this.createConn()
-                    }
-                }
-            })
-        },
-        changeLoginType() {
-            this.isQRCode = !this.isQRCode
-            if(this.isQRCode) this.getCode('qrcode')
-        },
-        showPrivacy(type) {
-            this.isPrivacy = true
-            this.loginType = type
-        },
-        toLogin(type) {
-            // 页面中跳转内容均取消,不支持,在登录之前需用户同意这两个条款(服务条款,隐私政策)
-            if(type === 'smspin' && (!this.loginForm.smspin || !this.loginForm.phone)) {
-                return
-            }
-            let params = {
-                type: type,
-            }
-            if(type === 'smspin') {
-                params.pin_code = this.loginForm.smspin
-                params.account = `+86-${this.loginForm.phone}`
-            } else if(type === 'qrcode') {
-                params.randomCode = this.qrCodeImg.randomCode
-            }
-            this.$api.loginCheck(params).then(res => {
-                if(res.code === 200) {
-                    localStorage.setItem('auth_token', res.x_auth_token)
-                    /* localStorage.setItem('access_token', res.implicit_token.access_token)
-                    localStorage.setItem('id_token', res.implicit_token.id_token)
-                    localStorage.setItem('expires_in', res.implicit_token.expires_in) */
-                    this.$router.push({path: '/admin'})
-                    this.$message({
-                        message: '登陆成功',
-                        type: 'success'
-                    });
-                }
-            })
-        },
-        getSchoolList() {
-            this.$api.getSchoolList({}).then(res => {
-                this.schoolList = res.schools
-            })
-        },
-        async bindSchool() {
-            let params = {
-                fp: await this.$tools.getFingerprint(),
-                id: this.schoolInfo.id,
-                name: this.schoolInfo.name,
-            }
-            this.$api.bindSchool(params).then(res => {
-                if(res.code === 200) {
-                    this.$message({
-                        message: '已成功绑定学校',
-                        type: 'success'
-                    });
-                    res.data.server.host = []
-                    res.data.server.uris.forEach(item => {
-                        res.data.server.networks.forEach(network => {
-                            // https://192.168.8.140:5001/login/student
-                            let url = `${item.protocol}://${network.ip}:${item.port}/login/student`
-                            res.data.server.host.push(url)
-                        })
-                    })
-                    res.data.server.shwoRam = (res.data.server.ram / 1024 / 1024 / 1024).toFixed(1)
-                    res.data.server.cpuInfos.forEach(item => {
-                        item.showHZ = item.hz ? (item.hz / 1000) : 0
-                    });
-                    localStorage.setItem('deviceId', res.data.device)
-                    this.deviceInfo = res.data
-                    this.hybridType = res.data?.hybrid
-                    this.isBindSchool = false
-                    this.schoolInfo = {
-                        id: '',
-                        name: '',
-                    }
-                } else {
-                    this.$message({
-                        message: '绑定学校失败,请检查学校名称和学校简码',
-                        type: 'error'
-                    });
-                }
-            })
-        },
-        createConn() {
-            /* 
-                grant_type取值:
-                public static readonly string _Message_grant_type_check_file = "check_file"; //检查文件
-                public static readonly string _Message_grant_type_ies_qrcode_login = "ies_qrcode_login"; //二维码扫描登陆
-                public static readonly string _Message_grant_type_download_file = "download_file"; //下载评测试卷
-                public static readonly string _Message_grant_type_upload_data = "upload_data"; //推送学生作答
-            */
-            this.signalR = new SignalRService()
-            this.signalR.initSignalR(`/signalr/exam?grant_type=ies_qrcode_login&clientid=${this.deviceInfo.device}`)
-            this.signalR.onReceiveConnection((message) => {
-                console.log('222222222', message);
-            })
-            this.signalR.onMessageReceived((message) => {
-                // status: -1 error(红),0 info(黑、白),1 success(绿),2 warning(黄)
-                if(message.status === 1) {
-                    this.qrCodeToken = JSON.parse(message.content)
-                }
-            })
-        },
-    },
-    computed: {
-        bindSchoolType() {
-            return !!this.deviceInfo?.server.school
-        },
-    },
-    beforeDestroy() {
-        this.signalR.stopSignalR()
-    },
-}
-</script>
-
-<style lang="less" scoped>
-.login {
-    width: 100%;
-    height: 100%;
-    background-repeat: no-repeat;
-    background-attachment: fixed;
-    background-position: center;
-    background-size: cover;
-    background: #F4F7FF;
-    .login-body {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        height: 100%;
-
-        .login-box {
-            border-radius: 20px;
-            background: #fff;
-            overflow: hidden;
-            display: flex;
-            justify-content: center;
-            // align-items: center;
-            margin-top: -50px;
-            box-shadow: 0px 2px 30px rgba(0, 0, 0, 0.15);
-            height: 60%;
-            width: 60%;
-            position: relative;
-
-            .body-left {
-                // width: 800px;
-                width: 60%;
-                text-align: center;
-                position: relative;
-
-                .bind-school {
-                    font-size: 25px;
-                    margin: 40px 0 20px;
-                }
-
-                img {
-                    width: 50%;
-                    // height: 100%;
-                    max-height: 100%;
-                }
-
-                .no-bind-school {
-                    font-size: 30px;
-                    position: absolute;
-                    top: 50%;
-                    transform: translateY(-50%);
-                    left: 50%;
-                    transform: translate(-50%, -50%);
-                }
-            }
-            .body-right {
-                width: calc(40% - 100px);
-                height: auto;
-                position: relative;
-                margin: 6% 50px;
-
-                /* .right-input {
-                    margin: 30px 100px;
-                } */
-
-                h1{
-                    margin-bottom: 50px;
-                    color: #5b5b5b;
-                }
-                .el-input {
-                    margin-bottom: 20px;
-                }
-
-                .el-button {
-                    // padding: 0 50px;
-                    // border-radius: 50px;
-                    width: 100%;
-                    margin: 10px 0;
-                }
-
-                .register-forget {
-                    font-size: 13px;
-                    color: #919191;
-                    text-align: right;
-                    margin-bottom: 10px;
-
-                    &>span {
-                        cursor: pointer;
-                        &:hover {
-                            color: #409EFF;
-                        }
-                    }
-                }
-                
-                .network-info {
-                    position: absolute;
-                    bottom: -10%;
-                    right: 0;
-                    width: 100%;
-                    text-align: center;
-                    margin: auto;
-
-                    span {
-                        cursor: pointer;
-                    }
-                }
-
-                .switch-school {
-                    /* position: absolute;
-                    bottom: -10%;
-                    right: -30px; */
-                    cursor: pointer;
-                    color: #ababab;
-
-                    &:hover {
-                        color: #409EFF;
-                    }
-                }
-            }
-        }
-    }
-    .qr-code {
-        position: absolute;
-        top: 20px;
-        right: 20px;
-        cursor: pointer;
-
-        img {
-            width: 75px;
-        }
-    }
-}
-.login-footer {
-    width: 100%;
-    position: fixed;
-    bottom: 10px;
-    z-index: 100;
-    left: 0;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-
-    .footer-info-item {
-        color: #919191;
-        margin-right: 40px;
-        font-size: 12px;
-    }
-}
-
-@media screen and (max-width: 1280px) {
-    .login .login-body .login-box {
-        .body-left {
-            display: none;
-        }
-        .body-right {
-            width: 100%;
-        }
-    }
-}
-
-@media screen and (max-width: 768px) {
-    .login .login-body .login-box {
-        width: 100%;
-        height: 100%;
-        margin-top: auto;
-        border-radius: 0;
-
-        .body-right {
-            margin: 6% 25px;
-
-            .network-info {
-                bottom: 1%;
-            }
-        }
-    }
-}
-</style>
-
-<style lang="less">
-.login {
-    .el-divider__text {
-        color: #919191;
-    }
-    .el-input__wrapper {
-        background-color: #ededed;
-        border: none;
-    }
-    .el-drawer {
-        min-width: 600px;
-    }
-}
-@media screen and (max-width: 768px) {
-    .login .el-drawer {
-        min-width: auto;
-        width: 100% !important;
-    }
-}
-</style>

+ 0 - 10
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/login/Index.vue

@@ -1,10 +0,0 @@
-<template>
-    <router-view></router-view>
-</template>
-
-<script>
-export default {}
-</script>
-
-<style>
-</style>

+ 0 - 13
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/login/Student.vue

@@ -1,13 +0,0 @@
-<template>
-    <div>学生端
-        会获取到学校信息、正在进行的活动,此处会展示活动名称、学校信息、账号密码
-        若没有活动,则不展示账号密码,提示没有活动
-    </div>
-</template>
-
-<script>
-export default {}
-</script>
-
-<style>
-</style>

+ 0 - 26
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/ActivityAnswer.less

@@ -1,26 +0,0 @@
-.el-header {
-    width: 100%;
-    height: 40px !important;
-    line-height: 40px;
-    background-color: #ffffff;
-    color: #24b880;
-    font-size: 20px;
-    // padding: 5px 15px;
-    position: relative;
-    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
-    box-sizing: content-box;
-}
-
-.el-main {
-    background-color: #ffffff;
-    color: #333;
-    display: flex;
-    .question-content {
-        width: 80%;
-        background-color: #B3C0D1;
-    }
-    .answer-sheet {
-        width: 20%;
-        background-color: antiquewhite;
-    }
-}

+ 0 - 353
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/ActivityAnswer.vue

@@ -1,353 +0,0 @@
-<template>
-    <el-container>
-        <el-header>
-            <i class="el-icon-tuichu2"></i>
-            <span>{{ activityInfo.name }}</span>
-            <span class="count-down" v-if="needCountDown && showExam.length">
-                <span>作答时间&nbsp;</span>
-                <span :style="{'color': diffSeconds < 60 ? 'red' : ''}">{{ surplus }}</span>
-            </span>
-        </el-header>
-        <el-main>
-            <!-- 附件展示 -->
-            <div v-if="instantPaper">
-                <div v-for="(img, index) in imgList" :key="index">
-                    <img :src="img" alt="">
-                </div>
-            </div>
-            <div class="question-content">
-                <div v-if="!showExam.length">暂没有试题</div>
-                <template v-else>
-                    <!-- 整卷作答 -->
-                    <div v-if="instantPaper || entireExam">
-                        <div v-for="(item, index) in showExam" :key="index" ref="questionBox">
-                            <div v-if="instantPaper">
-                                <div>
-                                    <span v-if="!['single', 'multiple', 'judge'].includes(item.type)">{{ index + 1 }}. </span>
-                                    <div v-if="item.parent === undefined && (index === 0 ? true :
-                                            ((item.type === 'subjective' && showExam[index-1].type === 'subjective') ?
-                                            item.answerType != showExam[index-1].answerType : item.type != showExam[index-1].type))"
-                                    >
-                                        <span>{{ item.typeName }}(处理数据,给类型赋名)</span>
-                                        <span v-if="item.type === 'subjective' && item.answerType">-{{ item.answerType }}(此处也要处理)</span>
-                                    </div>
-                                </div>
-                            </div>
-                            <div v-else>
-                                <div v-if="item.parent === undefined">
-                                    <span>{{ item.typeName }}</span>
-                                    <span v-if="item.type === 'subjective' && showExam[index].answerType">-{{ showExam[index].answerType }}</span>
-                                </div>
-                                <div class="que-item" v-if="item.type != 'compose' && item.parent === undefined">
-                                    <span>{{ index + 1 }}.</span>
-                                    <div id="answer-box" v-html="item.question"></div>
-                                </div>
-                                <!-- 综合题 -->
-                                <div v-if="item.type === 'compose' || item.parent !== undefined">
-                                    <div class="compose-content" v-if="item.parent !== undefined">
-                                        <div class="question-type" v-show="entireExam ? item.pid != showExam[index-1].pid : true">
-                                            <span>{{ item.parentInfo.typeName }}</span>
-                                            <span v-if="item.type === 'subjective' && showExam[index].answerType">({{ showExam[index].answerType }})</span>
-                                        </div>
-                                        <div class="compose-box" v-show="entireExam ? item.pid != showExam[index-1].pid : true">
-                                            <span style="margin-left:10px;font-weight:800;font-size:15px">题目:</span>
-                                            <br />
-                                            <div class="compose-item">
-                                                <div v-html="item.parentInfo.question"></div>
-                                            </div>
-                                        </div>
-                                        <div class="que-content">
-                                            <span style="width:51px">{{ item.paperIndex }}:</span>
-                                            <div class="que-items" id="answer-box" v-html="item.question"></div>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                            <div>
-                                <div class="questionNo" v-if="!instantPaper">我的作答:</div>
-                                <div>
-                                    <!--判断题选项-->
-                                    <div v-if="item.type == 'judge'" align="center">
-                                        <span style="font-weight: 900;" v-if="instantPaper">{{ index + 1 }}. </span>
-                                        <label class="testBtn yesNoBtn">
-                                            <input type="radio" value="A" v-model="checkers[index][0]" :disabled="!closeTest" />
-                                            <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[index][0] === 'A' ? (item.answer[0] === 'A' ? '' : 'red !important') : ''}">
-                                                <i class="el-icon-gou1"></i>
-                                            </div>
-                                        </label>
-                                        <label class="testBtn yesNoBtn">
-                                            <input type="radio" value="B" v-model="checkers[index][0]" :disabled="!closeTest" />
-                                            <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[index][0] === 'B' ? (item.answer[0] === 'B' ? '' : 'red !important') : ''}">
-                                                <i class="el-icon-chacha"></i>
-                                            </div>
-                                        </label>
-                                    </div>
-                                    <!--选择题选项-->
-                                    <div class="select-box" v-else-if="item.type === 'single' || item.type === 'multiple'">
-                                        <span style="font-weight: 900;" v-if="instantPaper">{{ index + 1 }}. </span>
-                                        <label class="testBtn" v-for="(option, oIndex) in item.option" :key="oIndex">
-                                            <input type="checkbox" :value="option.code" v-model="checkers[index]" @click="getAns(index, oIndex)" :disabled="!closeTest" />
-                                            <div class="testbg">
-                                                <div style="display:flex">
-                                                    <span>{{ option.code }}</span>
-                                                    <div v-html="option.value" @click.stop.native.prevent="showImg($event)"></div>
-                                                    <span v-show="showEnd" style="margin-left: 5px;">
-                                                        <Icon type="md-checkmark-circle" color="#44b5f9" size="20" v-if="item.answer.includes(option.code)" />
-                                                        <Icon type="md-close-circle" color="red" size="20" v-if="checkers[index].includes(option.code) && !item.answer.includes(option.code)" />
-                                                    </span>
-                                                </div>
-                                                <div style="clear:both"></div>
-                                            </div>
-                                        </label>
-                                    </div>
-                                    <!--问答题-->
-                                    <div class="compose-content" v-else-if="item.type === 'subjective'">
-                                        <Compose v-if="item.answerType === 'text' || item.answerType === 'text_Image'" ref="compose" :itemInfo="item" :close="!closeTest" :textData="checkers[index]" :index="index" @dataGet="getComposeAns"></Compose>
-                                        <template v-else-if="item.answerType === 'audio'">
-                                            <AudioRecorder :textData="checkers[index]" :index="index" @dataGet="getComposeAns" />
-                                        </template>
-                                        <template v-else-if="item.answerType === 'file' || item.answerType === 'image'">
-                                            <Upload type="drag" :accept="subjectiveAccept(item)" action="" :before-upload="file => customUpload(file, index)">
-                                                <div style="padding: 20px 0" ref="upload1">
-                                                    <Icon type="ios-cloud-upload" size="52" :style="{'color': checkers[index].length ? '#b4b4b4' : '#2d8cf0'}"></Icon>
-                                                    <p>
-                                                        <span>
-                                                            {{ checkers[index].length ? '重新上传' : '上传相关文件' }}
-                                                        </span>
-                                                    </p>
-                                                </div>
-                                            </Upload>
-                                            <div class="repair-link-wrap-item-box" v-if="checkers[index].length">
-                                                <div class="file-icon">
-                                                    <img :src="$tools.getFileThum(getFileType(checkers[index][0]), getFileName(checkers[index][0]))"/>
-                                                </div>
-                                                <div class="file-info">
-                                                    <p class="file-name">{{ getFileName(checkers[index][0]) }}</p>
-                                                    <div>
-                                                        <span @click="onPreview(checkers[index][0], true)">预览</span>
-                                                        <span @click="onDownload(checkers[index][0], true)">下载</span>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </template>
-                                        <div class="compose-content" v-else>
-                                            <Compose ref="compose" :itemInfo="item" :close="!closeTest" :textData="checkers[index]" :index="index" @dataGet="getComposeAns"></Compose>
-                                        </div>
-                                    </div>
-                                    <div class="compose-content" v-else>
-                                        <Compose ref="compose" :itemInfo="item" :close="!closeTest" :textData="checkers[index]" :index="index" @dataGet="getComposeAns"></Compose>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-                    <!-- 单题作答 -->
-                    <div v-if="!instantPaper && !entireExam">
-                        <div ref="questionBox">
-                            <div>
-                                <div v-if="showQuesInfo.parent === undefined">
-                                    <span>{{ showQuesInfo.typeName }}</span>
-                                    <span v-if="showQuesInfo.type === 'subjective' && showExam[queNo].answerType">-{{ showExam[queNo].answerType }}</span>
-                                </div>
-                                
-                                <div class="que-item" v-if="showQuesInfo.type != 'compose' && showQuesInfo.parent === undefined">
-                                    <span>{{ queNo + 1 }}.</span>
-                                    <div id="answer-box" v-html="showQuesInfo.question"></div>
-                                </div>
-                                <!--综合题-->
-                                <div v-if="showQuesInfo.type === 'compose' || showQuesInfo.parent !== undefined">
-                                    <div class="compose-content" v-if="showQuesInfo.parent !== undefined">
-                                        <div class="questionType">
-                                            <span>{{ showQuesInfo.parentInfo.typeName }}</span>
-                                            <span v-if="showQuesInfo.type === 'subjective' && showExam[queNo].answerType">({{ showExam[queNo].answerType }})</span>
-                                        </div>
-                                        <div class="compose-box">
-                                            <span style="margin-left:10px;font-weight:800;font-size:15px">题目:</span>
-                                            <br />
-                                            <div class="compose-item">
-                                                <div v-html="showQuesInfo.parentInfo.question"></div>
-                                            </div>
-                                        </div>
-                                        <div class="que-content">
-                                            <span style="width:51px">{{ showQuesInfo.paperIndex }}:</span>
-                                            <div class="que-items" id="answer-box" v-html="showQuesInfo.question"></div>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                            <div class="answers">
-                                <div class="questionNo">我的作答:</div>
-                                <div class="answers-box">
-                                    <!--判断题选项-->
-                                    <div v-if="showQuesInfo.type == 'judge'" align="center">
-                                        <label class="testBtn yesNoBtn">
-                                            <input type="radio" value="A" v-model="checkers[queNo][0]" :disabled="!closeTest" />
-                                            <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[queNo][0] === 'A' ? (showQuesInfo.answer[0] === 'A' ? '' : 'red !important') : ''}">
-                                                <!-- <Icon type="ios-radio-button-off" /> -->
-                                                <Icon type="md-checkmark" />
-                                            </div>
-                                        </label>
-                                        <label class="testBtn yesNoBtn">
-                                            <input type="radio" value="B" v-model="checkers[queNo][0]" :disabled="!closeTest" />
-                                            <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[queNo][0] === 'B' ? (showQuesInfo.answer[0] === 'B' ? '' : 'red !important') : ''}">
-                                                <Icon type="md-close" />
-                                            </div>
-                                        </label>
-                                    </div>
-                                    <!--选择题选项-->
-                                    <div class="select-box" v-else-if="showQuesInfo.type === 'single' || showQuesInfo.type === 'multiple'">
-                                        <label class="testBtn" v-for="(item, index) in showQuesInfo.option" :key="index">
-                                            <span v-show="showEnd">
-                                                <Icon type="md-checkmark-circle" color="#24B880" size="20"
-                                                    v-if="showQuesInfo.answer.includes(item.code)"
-                                                />
-                                                <Icon type="md-close-circle" color="red" size="20"
-                                                    v-if="checkers[queNo].includes(item.code) && !showQuesInfo.answer.includes(item.code)"
-                                                />
-                                            </span>
-                                            <input type="checkbox"
-                                                :value="showQuesInfo.option[index].code"
-                                                v-model="checkers[queNo]"
-                                                @click="getAns(queNo,index)"
-                                                :disabled="!closeTest" />
-                                            <div class="testbg">
-                                                <div style="display:flex">
-                                                    <span>{{ showQuesInfo.option[index].code }}. </span>
-                                                    <div v-html="item.value" @click.stop.native.prevent="showImg($event)"></div>
-                                                </div>
-                                                <div style="clear:both"></div>
-                                            </div>
-                                        </label>
-                                    </div>
-                                    <!--问答题-->
-                                    <div class="compose-content" v-else-if="showQuesInfo.type === 'subjective'">
-                                        <Compose v-if="showExam[queNo].answerType === 'text' || showExam[queNo].answerType === 'text_Image'" ref="compose" :itemInfo="showQuesInfo" :close="!closeTest" :textData="checkers[queNo]" :index="queNo" @dataGet="getComposeAns"></Compose>
-                                        <template v-else-if="showExam[queNo].answerType === 'audio'">
-                                            <AudioRecorder :textData="checkers[queNo]" :index="queNo" @dataGet="getComposeAns" />
-                                        </template>
-                                        <template v-else-if="showExam[queNo].answerType === 'file' || showExam[queNo].answerType === 'image'">
-                                            <Upload type="drag" :accept="subjectiveAccept(showExam[queNo])" action="" :before-upload="file => customUpload(file, queNo)">
-                                                <div style="padding: 20px 0" ref="upload1">
-                                                    <Icon type="ios-cloud-upload" size="52" :style="{'color': checkers[queNo].length ? '#b4b4b4' : '#2d8cf0' }"></Icon>
-                                                    <p>
-                                                        <span>
-                                                            {{ checkers[queNo].length ? '重新上传' : '上传相关文件'}}
-                                                        </span>
-                                                    </p>
-                                                </div>
-                                            </Upload>
-                                            <div class="repair-link-wrap-item-box" v-if="checkers[queNo].length">
-                                                <div class="file-icon">
-                                                    <img :src="$tools.getFileThum(getFileType(checkers[queNo][0]), getFileName(checkers[queNo][0]))"/>
-                                                </div>
-                                                <div class="file-info">
-                                                    <p class="file-name">{{ getFileName(checkers[queNo][0]) }}</p>
-                                                    <div>
-                                                        <span @click="onPreview(checkers[queNo][0], true)">{{ $t('ability.review.preview')}}</span>
-                                                        <span @click="onDownload(checkers[queNo][0], true)">{{ $t('ability.review.download')}}</span>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </template>
-                                        <div class="compose-content" v-else>
-                                            <Compose ref="compose" :itemInfo="showQuesInfo" :close="!closeTest" :textData="checkers[queNo]" :index="queNo" @dataGet="getComposeAns"></Compose>
-                                        </div>
-                                    </div>
-                                    <div class="compose-content" v-else>
-                                        <Compose ref="compose" :itemInfo="showQuesInfo" :close="!closeTest" :textData="checkers[queNo]" :index="queNo" @dataGet="getComposeAns"></Compose>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-                </template>
-            </div>
-            <div class="answer-sheet">
-                <div v-if="needCountDown">
-                    <span>作答时间:{{ surplus }}</span>
-                </div>
-                <div v-if="widthLimit">
-                    <div>
-                        <div>
-                            <span>我的答题卡</span>
-                            <el-switch style="display: block" v-model="entireExam" active-color="#13ce66"
-                                active-text="整卷作答" inactive-text="单题作答">
-                            </el-switch>
-                        </div>
-                        <div>
-                            <div v-if="showExam.length">
-                                <div>
-                                    <span style="margin-right: 10px;">
-                                        <el-button type="info" size="mini" plain>
-                                            <i class="el-icon-arrow-left"></i>
-                                            上一题
-                                        </el-button>
-                                    </span>
-                                    <span>
-                                        <el-button type="info" size="mini" plain>
-                                            下一题
-                                            <i class="el-icon-arrow-right"></i>
-                                        </el-button>
-                                    </span>
-                                </div>
-                                <p>题号:</p>
-                                <span v-for="(item, index) in showExam" :key="index" style="padding:5px 5px;cursor:pointer">
-                                    {{ item.parent === undefined ? index + 1 : item.paperIndex }}
-                                </span>
-                                <p>
-                                    <el-button type="info" size="mini" plain>信息按钮</el-button>
-                                </p>
-                            </div>
-                            <div v-else>暂没有试题</div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        </el-main>
-        <div class="top-icon" @click="gotoTop" v-if="instantPaper || entireExam">
-            <i class="el-icon-arrow-up" style="font-size: 30px; color: #249e35;"></i>
-        </div>
-    </el-container>
-</template>
-
-<script>
-import AudioRecorder from './AudioRecorder.vue';
-export default {
-    components: {
-        AudioRecorder
-    },
-    data() {
-        return {
-            loading: false,
-            activityInfo: {
-                name: '艺术评测'
-            },
-            needCountDown: true,
-            showExam: [{}, {}, {}],
-            surplus: "", //倒计时的字符
-            diffSeconds: 0, //秒数
-            entireExam: false,
-            widthLimit: true,
-            instantPaper: false,
-            showQuesInfo: undefined,
-        }
-    },
-    methods: {
-        startLoading() {
-            this.loading = this.$loading({
-                lock: true,
-                text: 'Loading',
-                spinner: 'el-icon-loading',
-                background: 'rgba(0, 0, 0, 0.8)'
-            })
-        },
-        closeLoading() {
-            this.loading.close()
-        },
-    }
-}
-</script>
-
-<style lang="less" scoped>
-@import "./ActivityAnswer.less";
-</style>

+ 0 - 150
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/ActivityInfo.less

@@ -1,150 +0,0 @@
-.el-container {
-    background-color: #ffffff;
-    position: relative;
-
-    .el-header {
-        background-color: #b3c0d1;
-        // color: #333;
-        line-height: 60px;
-    }
-
-    .el-main {
-        // background-color: #ffffff;
-        // color: #333;
-        width: 65%;
-        height: 100%;
-        margin: auto;
-        padding: 40px;
-
-        .header-box {
-            display: flex;
-            justify-content: space-between;
-
-            .base-info {
-                font-size: 14px;
-
-                p {
-                    margin-bottom: 5px;
-
-                    &>span {
-                        font-weight: bold;
-                        color: #00ad6c;
-                        margin-right: 5px;
-                    }
-                }
-            }
-        }
-
-        .title-mark {
-            background: #00ad6c;
-            padding: 4px 13px;
-            margin-right: 10px;
-            color: #fff;
-            border-radius: 18px;
-        }
-
-        .paper-subject {
-            font-weight: bolder;
-            line-height: 40px;
-            margin-right: 20px;
-            cursor: pointer;
-            display: inline-flex;
-
-            &>span:first-child {
-                background-color: #00ad6c;
-                padding: 0 15px;
-                color: #fff;
-                border-radius: 5px 0 0 5px;
-            }
-
-            &>span:nth-of-type(2) {
-                padding: 0 15px;
-                border: 1px solid #ababab;
-                border-radius: 0 5px 5px 0;
-                border-color: #00ad6c;
-            }
-
-            .el-icon-circle-check {
-                color: #00ad6c;
-                font-size: 18px;
-                margin-left: 15px;
-            }
-        }
-
-        .paper-content {
-            margin-top: 50px;
-        }
-
-
-        .art-content {
-            margin: 10px 0;
-            // height: 100%;
-
-            .subject-content {
-                margin-bottom: 50px;
-
-                .subject-title {
-                    width: calc(100% - 20px);
-                    background-color: #79b29c;
-                    color: #fff;
-                    font-size: 20px;
-                    padding: 10px;
-                    border-radius: 5px;
-                    margin: 20px 0;
-                }
-
-                .paper-answer {
-                    .scoreboard {
-                        // width: 100%;
-                        background: rgb(240, 240, 240);
-                        border-radius: 8px;
-                        padding: 15px;
-
-                        .ivu-card {
-                            margin-bottom: 20px;
-                        }
-
-                        .to-answer {
-                            // font-size: 25px;
-                            font-weight: 800;
-                            padding: 17px;
-                            cursor: pointer;
-
-                            .answer-type {
-                                margin-left: 50px;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    .loading-container {
-        position: absolute;
-        width: 100%;
-        height: 100%;
-        right: 0;
-        top: 0;
-        display: flex;
-        flex-direction: row;
-        justify-content: center;
-        /*background: rgba(103, 103, 103, 0.27);*/
-        z-index: 1000;
-
-        #loadingBox {
-            height: 200px;
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            align-items: center;
-            background: #ffffff;
-            padding: 50px;
-            border-radius: 10px;
-
-            .el-progress__text {
-                font-size: 30px;
-            }
-        }
-    }
-}

+ 0 - 223
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/ActivityInfo.vue

@@ -1,223 +0,0 @@
-<template>
-    <el-container>
-        <div class="loading-container" style="background: rgba(0, 0, 0, 0.7)" v-show="isLoadQues">
-            <div id="loadingBox" style="margin: auto;">
-                <el-progress type="circle" :stroke-width="8" :width="150" define-back-color="#fff" text-color="#07ac88" color="#07ac88" :percentage="80"></el-progress>
-                <div style="color: #00ad6c; margin: 30px 0px 15px; font-size: 20px;">试题载入中,请耐心等待</div>
-                <span style="font-size: 12px; color: #666 !important;">若长时间卡住,请 <a href="#" @click.prevent="handleLink()" style="color: #1487ff;">刷新页面</a> 后重试 </span>
-            </div>
-        </div>
-        <el-header>Header</el-header>
-        <el-main>
-            <div class="header-box">
-                <div style="font-weight: bold; font-size: 1.5em;">
-                    <span class="title-mark">评测</span>
-                    <span>评测名称</span>
-                </div>
-                <div class="base-info">
-                    <p>
-                        <i class="el-icon-user"></i>
-                        课程老师:<span>罗老师</span>
-                    </p>
-                    <p>
-                        <i class="el-icon-postcard"></i>
-                        学生名单:<span>2021级3班</span>
-                    </p>
-                    <p>
-                        <i class="el-icon-user"></i>
-                        学生姓名:<span>鹿晗</span>
-                    </p>
-                    <p>
-                        <i class="el-icon-time"></i>
-                        活动时间:<span>2024-12-20 11:00 ~ 2025-01-20 11:00</span>
-                    </p>
-                </div>
-            </div>
-            <template v-if="activityInfo.type === 'Exam' || (activityInfo.type === 'Art' && activityInfo.progress === 'finish')">
-                <div style="margin-top: 30px;">
-                    <div v-for="(item, index) in subjectList" :key="index" class="paper-subject">
-                        <span>{{ item }}</span>
-                        <span>
-                            {{ item }}科试卷
-                            <i class="el-icon-circle-check"></i>
-                        </span>
-                    </div>
-                </div>
-                <div class="paper-content">
-                    <el-tabs v-model="activeName">
-                        <el-tab-pane label="评测内容" name="exam">
-                            
-                        </el-tab-pane>
-                        <el-tab-pane label="评测内容" name="111">用户管理</el-tab-pane>
-                    </el-tabs>
-                </div>
-            </template>
-            <template v-if="activityInfo.type === 'Art' && activityInfo.progress === 'going'">
-                <div class="art-content">
-                    <div class="subject-content" v-for="item in artExam" :key="item.id">
-                        <p class="subject-title">{{ item.subject.name }}</p>
-                        <div class="paper-answer" v-if="item.exam[0]">
-                            <div class="scoreboard">
-                                <div v-show="item.testState === 1" @click="onLoadQues(item)" class="to-answer">
-                                    <i class="el-icon-dianji" style="font-size: 35px; color: #03966a;"></i>
-                                    <span style="color: #03966a; margin-left: 10px; font-size: 25px;">前往作答</span>
-                                    <i class="el-icon-weizuoda answer-type" style="font-size: 50px;"></i>
-                                </div>
-                                <div v-show="item.testState === 2 || item.testState === 3" class="to-answer">
-                                    <i class="el-icon-gougou1" style="color: green;"></i>
-                                    <span style="margin-left: 5px;">成绩尚未结算,请等待老师批改试卷,统计成绩</span>
-                                    <i class="el-icon-yiwancheng answer-type" style="font-size: 50px; color: #01adff;"></i>
-                                </div>
-                            </div>
-                            <template v-if="item.homework.length">
-                                <div v-for="(hw, index) in item.homework" :key="index">
-                                    <template v-if="hw">
-                                        <template v-if="hw.subject === 'subject_music' && hw.quotaId === 'quota_22'">
-                                            <div class="scoreboard">
-                                                <!-- <div v-if="hw.overTime && !hw.isAnswer" class="to-answer"> -->
-                                                    <div class="to-answer">
-                                                    <i class="el-icon-warning-outline"></i>
-                                                    <span style="margin-left: 5px;">已结束</span>
-                                                    <i class="el-icon-weizuoda answer-type" style="font-size: 50px;"></i>
-                                                </div>
-                                            </div>
-                                                <div class="scoreboard">
-                                                <!-- <div v-else-if="!hw.overTime && hw.isAnswer" class="to-answer"> -->
-                                                    <div class="to-answer">
-                                                    <i class="el-icon-gougou1" style="color: green;"></i>
-                                                    <span style="margin-left: 5px;">已完成作答</span>
-                                                    <i class="el-icon-yiwancheng answer-type" style="font-size: 50px; color: #01adff;"></i>
-                                                </div></div>
-                                                <div class="scoreboard">
-                                                <!-- <div v-else-if="hw.overTime && hw.isAnswer" class="to-answer"> -->
-                                                    <div class="to-answer">
-                                                    <i class="el-icon-dianji" style="font-size: 35px; color: #03966a;"></i>
-                                                    <span style="color: #03966a; margin-left: 10px; font-size: 25px;">查看详情</span>
-                                                </div></div>
-                                                <!-- <div v-else class="to-answer"> -->
-                                                    <div class="scoreboard">
-                                                    <div class="to-answer">
-                                                    <i class="el-icon-dianji" style="font-size: 35px; color: #03966a;"></i>
-                                                    <span style="color: #03966a; margin-left: 10px; font-size: 25px;">开始演唱</span>
-                                                    <i class="el-icon-weizuoda answer-type" style="font-size: 50px;"></i>
-                                                </div>
-                                            </div>
-                                        </template>
-                                    </template>
-                                </div>
-                            </template>
-                        </div>
-                        <div v-else class="paper-answer">
-                            暂无科目相关内容
-                        </div>
-                    </div>
-                </div>
-            </template>
-        </el-main>
-    </el-container>
-</template>
-
-<script>
-export default {
-    data() {
-        return {
-            isLoadQues: false,
-            loading: undefined,
-            subjectList: ['语文', '数学'],
-            activeName: 'exam',
-            activityInfo: {
-                type: 'Art',
-                progress: 'going'
-            },
-            artExam: [
-                {
-                    subject: {name: '语文'},
-                    exam: [{}],
-                    testState: 2,
-                    homework: [],
-                },
-                {
-                    subject: {name: '音乐'},
-                    exam: [{}],
-                    testState: 1,
-                    homework: [
-                        {
-                            subject: 'subject_music',
-                            quotaId: 'quota_22',
-                            overTime: '',
-                            isAnswer: true
-                        }
-                    ],
-                }
-            ],
-            processNum: 0,
-            isUpload: false
-        }
-    },
-    methods: {
-        startLoading() {
-            this.loading = this.$loading({
-                lock: true,
-                text: 'Loading',
-                spinner: 'el-icon-loading',
-                background: 'rgba(0, 0, 0, 0.8)'
-            })
-        },
-        closeLoading() {
-            this.loading.close()
-        },
-        handleLink() {
-            window.location.reload()
-        },
-        async onLoadQues(art) {
-            this.processNum = 0
-            this.isLoadQues = true
-            /* let infos = await this.getPaper(art.subject.id)
-            this.showTest(art) */
-        },
-    }
-}
-</script>
-
-<style lang="less" scoped>
-@import "./ActivityInfo.less";
-</style>
-
-<style lang="less">
-.paper-content {
-    .el-tabs__active-bar {
-        display: none;
-    }
-
-    .el-tabs__item.is-active {
-        color: #24b880;
-        font-weight: bolder;
-        border-bottom: 4px solid #24b880 !important;
-        margin: 0 10px;
-        width: auto;
-        text-align: center !important;
-        font-size: 24px;
-    }
-
-    .el-tabs__item {
-        text-align: center !important;
-        font-size: 24px;
-        margin: 0 10px;
-        font-weight: bolder;
-        color: #5a5a5a;
-
-        &:hover {
-            color: #24b880;
-        }
-    }
-
-    .el-tabs--top .el-tabs__item.is-top{
-        padding: 5px 20px;
-        height: 100%;
-    }
-}
-
-.loading-container .el-progress__text {
-    font-size: 30px !important;
-}
-</style>

+ 0 - 197
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/view/student/AudioRecorder.vue

@@ -1,197 +0,0 @@
-<template>
-    <div>
-        <div class="audio-box" v-if="audioType === 2">
-            <audio controls :key="audioKey" :id="`audioId${audioId}`">
-                <source :src="fileFullPath">
-                {{ $t('teachContent.notAudio') }}
-            </audio>
-            <Icon custom="iconfont icon-shuaxin1" @click="startRecorder(true)" />
-        </div>
-        <div class="audio-recorder">
-            <p v-show="audioType != 2">{{ stuDuration }}</p>
-            <Icon custom="iconfont icon-luyin" v-show="!audioType" @click="startRecorder()" />
-            <Icon custom="iconfont icon-luyin-zanting" v-show="audioType === 1" @click="stopRecorder()" />
-            <!-- <Icon custom="iconfont icon-start" v-show="audioType === 2" /> -->
-        </div>
-    </div>
-</template>
-
-<script>
-import Recorder from 'js-audio-recorder'
-import BlobTool from '@/utils/blobTool.js'
-// import { mapGetters } from 'vuex'
-export default {
-    props: {
-        index: {
-            type: Number,
-            default: -1,
-        },
-        textData: {
-            type: Array,
-            default: () => {
-                return []
-            },
-        },
-    },
-    data () {
-        return {
-            recorder: undefined,
-            audioType: 0, //未开始(0) 进行中(1) 停止(2)
-            currentUrl: null,
-            answerUrl: [],
-            fileFullPath: '',
-            sasData: undefined,
-            audioId: '',
-            audioKey: '',
-        }
-    },
-    created () {
-        this.audioId = this.$jsFn.getBtwRandom(0, 100000000)
-        this.recorder = new Recorder({
-            sampleBits: 16,
-            sampleRate: 16000,
-            numChannels: 1,
-            // compiling: false,
-        })
-    },
-    async mounted () {
-        let { scope, cntr } = this.getComposeData
-        // getSchoolSas有加?
-        this.sasData = scope === 'school' ? await this.$tools.getSchoolSas(cntr) : await this.$api.blob.blobSasRCW({ name: cntr, role: 'teacher' })
-        this.sasData.sas = scope === 'school' ? this.sasData.sas : ('?' + this.sasData.sas)
-        this.getAnsInfo()
-    },
-    computed: {
-        /* ...mapGetters([
-            "getComposeData",
-        ]), */
-        stuDuration() {
-            if(this.recorder?.duration) {
-                // 分钟
-                let minutes = Math.floor(this.recorder.duration / 60) % 60
-                minutes = minutes >= 10 ? minutes : ("0" + minutes)
-                // 秒数
-                let seconds = Math.floor(this.recorder.duration % 60)
-                seconds = seconds >= 10 ? seconds : ("0" + seconds)
-                return `${minutes}:${seconds}`
-            } else {
-                return '00:00'
-            }
-        },
-    },
-    watch: {
-        index() {
-            this.recorder = undefined
-            this.recorder = new Recorder({
-                sampleBits: 16,
-                sampleRate: 16000,
-                numChannels: 1,
-                // compiling: false,
-            })
-            this.getAnsInfo()
-            deep: true
-            immediate: true
-        }
-    },
-    methods: {
-        getAnsInfo() {
-            this.audioType = 0
-            this.answerUrl = []
-            this.currentUrl = null
-            this.fileFullPath = ''
-            this.audioKey = this.$jsFn.getBtwRandom(0, 10000000000)
-            if(this.textData.length) {
-                let { cntr } = this.getComposeData
-                this.answerUrl = [...this.textData]
-                this.currentUrl = this.textData[0]
-                this.fileFullPath = `${this.sasData.url}/${cntr}${this.currentUrl}${this.sasData.sas}`
-                this.audioType = 2
-            }
-        },
-        startRecorder(isRefresh) {
-            // return
-            Recorder.getPermission().then(() => {
-                if(isRefresh) {
-                    /* this.fileFullPath = ''
-                    document.getElementById(`audioId${this.audioId}`).setAttribute('src', this.fileFullPath) */
-                }
-                this.recorder.start()
-                this.audioType = 1
-            }, error => {
-                console.log('报错:', error);
-                this.$Message.warning('请允许网页使用麦克风')
-            })
-        },
-        playRecorder() {
-            this.recorder.play()
-        },
-        async stopRecorder() {
-            this.recorder.stop()
-            if(!this.recorder || !this.recorder.duration) {
-                this.$Message.warning('请先录音')
-                return
-            }
-            this.audioType = 2
-            // return
-            // this.recorder.downloadWAV() //下载文件
-            // 停止录音后马上上传到blob
-            const blob = this.recorder.getWAVBlob()
-            const newBlob = new Blob([blob], {type: 'audio/wav'})
-            const fileBlob = new File([newBlob], `audio${this.index + 1}.wav`,{type: 'audio/wav'})
-            
-            let { scope, cntr, examId, subjectId, stuId } = this.getComposeData
-            let sas = '?' + this.sasData.sas
-            let containerClient = new BlobTool(this.sasData.url, this.sasData.name, this.sasData.sas, scope)
-            let path = `exam/${examId}/${subjectId}/${stuId}`
-            let that = this
-            containerClient.upload(fileBlob, {
-                path,
-                checkSize: false
-            }).then(res => {
-                that.currentUrl = res.blob
-                let random = this.$jsFn.getBtwRandom(0, 1000000)
-                that.fileFullPath = res.url + this.sasData.sas + '&t=' + random
-                document.getElementById(`audioId${that.audioId}`).setAttribute('src', that.fileFullPath)
-                that.$emit("dataGet", that.currentUrl, that.index)
-                that.$Message.warning(that.$t('cusMgt.saveOk'))
-            }).catch(err => {
-                console.log(err);
-                that.$Message.warning(that.$t('cusMgt.saveErr'))
-                that.fileFullPath = `${that.sasData.url}/${cntr}${that.currentUrl}${this.sasData.sas}`
-            })
-        },
-    }
-}
-</script>
-
-<style lang="less" scoped>
-.audio-recorder {
-    text-align: center;
-    width: 10rem;
-
-    &>p {
-        font-size: 30px;
-    }
-
-    .ivu-icon {
-        font-size: 10rem;
-        cursor: pointer;
-        color: #65af5b;
-    }
-}
-.audio-box {
-    display: flex;
-    align-items: center;
-
-    .ivu-icon {
-        margin-left: 10px;
-        font-size: 3rem;
-        cursor: pointer;
-        color: #888888;
-        &:hover {
-            color: #65af5b;
-        }
-    }
-}
-
-</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, // 禁用主机检查,允许局域网访问
-	} */
-})

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

+ 0 - 74
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/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 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
-    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

二進制
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

二進制
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

二進制
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

二進制
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

二進制
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-----

二進制
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/logo.png


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

+ 0 - 74
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/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 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
-    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

二進制
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-----

+ 0 - 104
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/BaseController.cs

@@ -1,104 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-
-namespace IES.ExamServer.Controllers
-{
-    public class BaseController : ControllerBase
-    {
-        public BaseController()
-        {
-
-
-        }
-        public string GetIP()
-        {
-            var IpPort = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
-
-            if (string.IsNullOrEmpty(IpPort))
-            {
-                IpPort = $"{HttpContext.Connection.RemoteIpAddress}";
-            }
-            if (IpPort.Contains("::"))
-            {
-                IpPort = "127.0.0.1";
-            }
-            return IpPort;
-        }
-        public string GetCookie(string key)
-        {
-            IRequestCookieCollection cookies = HttpContext.Request.Cookies;
-            string value = "";
-            if (cookies != null)
-            {
-                foreach (var ck in cookies)
-                {
-                    if (ck.Key.Equals(key))
-                    {
-                        value = ck.Value;
-                        break;
-                    }
-                }
-            }
-            return value;
-        }
-        /// <summary>
-        /// 取得AuthToken權杖資訊
-        /// </summary>        
-        /// <param name="key">Key Name</param>
-        /// <returns></returns>
-        public (string id, string? name, string picture, string school,string scope ,string timeZone,List<string> rolse, string keyData) GetAuthTokenInfo(string? key = null)
-        {
-            object? keyData = null;
-            HttpContext.Items.TryGetValue("ID", out object? id);
-            HttpContext.Items.TryGetValue("Name", out object? name);
-            HttpContext.Items.TryGetValue("Picture", out object? picture);
-            HttpContext.Items.TryGetValue("School", out object? school);
-            HttpContext.Items.TryGetValue("Scope", out object? scope);
-            HttpContext.Items.TryGetValue("TimeZone", out object? timeZone);
-            List<string> rolse= new List<string>();
-            if (HttpContext.Items.TryGetValue("Roles", out object? _roles)) 
-            {
-                if (_roles is List<string> s)
-                    {
-                    rolse=s;
-                }
-            }
-            if (!string.IsNullOrWhiteSpace(key))
-            {
-                HttpContext.Items.TryGetValue(key, out keyData);
-            }
-            return ($"{id}", $"{name}", $"{picture}", $"{school}",$"{scope}",$"{timeZone}", rolse, $"{keyData}");
-        }
-
-       
-        /// <summary>
-        /// 取得驗證金鑰,Authorization
-        /// </summary>        
-        public string GetToken()
-        {
-            return HttpContext.Request.Headers["Authorization"].ToString();
-        }
-
-        /// <summary>
-        /// 取得JWT驗證金鑰,Authorization Bearer
-        /// </summary>
-        /// <param name="httpContext"></param>
-        /// <returns></returns>
-        public string GetJwtToken()
-        {
-            var token = string.Empty;
-            string authorization = HttpContext.Request.Headers["Authorization"].ToString();
-            if (!string.IsNullOrWhiteSpace(authorization) && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
-            {
-                token = authorization.Substring("Bearer ".Length).Trim();
-            }
-            return token;
-        }
-
-
-
-        public   int code = 0;
-        public   string msg = "OK";
-
-
-    }
-}

+ 0 - 416
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/IndexController.cs

@@ -1,416 +0,0 @@
-using IES.ExamServer.Models;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Caching.Memory;
-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;
-
-namespace IES.ExamServer.Controllers
-{
-    [ApiController]
-    [Route("index")]
-  
-    public class IndexController : BaseController
-    {
-        private readonly IConfiguration _configuration;
-        private readonly IHttpClientFactory _httpClientFactory;
-        private readonly IMemoryCache _memoryCache;
-        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)
-        {
-            _logger = logger;
-            _configuration=configuration;
-            _httpClientFactory=httpClientFactory;
-            _memoryCache=memoryCache;
-            _connectionService=connectionService;
-            _liteDBFactory=liteDBFactory;
-        }
-        [HttpPost("generate-certificate")]
-        public async Task<IActionResult> GenerateCertificate(JsonNode json) 
-        {
-            try {
-                ServerDevice serverDevice = _memoryCache.Get<ServerDevice>(Constant._KeyServerDevice);
-                if (serverDevice!=null && serverDevice.networks.IsNotEmpty())
-                {
-                    string mac = $"{json["mac"]}";
-                    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 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");
-                        await System.IO.File.WriteAllTextAsync(pathCertificateBat, text);
-                        string pathCertificateCer = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package", "certificate.cer");
-                        System.IO.File.Copy(pathCer, pathCertificateCer);
-                        return Ok(new { code = 200, msg = "生成成功。", bat = "package/certificate.bat", cer = "package/certificate.cer" });
-                    }
-                    else
-                    {
-                        msg="未找到匹配的网卡设备。";
-                    }
-                }
-                else
-                {
-                    msg="服务端设备未找到,或网卡设备不存在。";
-                }
-            } catch (Exception ex) 
-            {
-                _logger.LogError($"生成证书和自定义域名映射脚本错误。{ex.Message},{ex.StackTrace}");
-                msg=$"服务端错误,{ex.Message}";
-            }
-            return Ok(new { code = 400, msg = msg });
-        }
-        [HttpPost("list-schools")]
-        public async Task<IActionResult> ListSchool(JsonNode json) 
-        {
-            string filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package", "schools.json");
-            string schoolText = await System.IO.File.ReadAllTextAsync(filePath);
-            JsonNode? node = schoolText.ToObject<JsonNode>();
-            return Ok(new {code =200, schools= node?["schools"] });
-        }
-        [HttpPost("bind-school")]
-        public async Task<IActionResult> BindSchool(JsonNode json) 
-        {
-            string id=$"{json["id"]}";
-            string name= $"{json["name"]}";
-            string fp = $"{json["fp"]}";
-            if (!string.IsNullOrWhiteSpace(id) && !string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(fp))
-            {
-                string filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package", "schools.json");
-                string schoolText = await System.IO.File.ReadAllTextAsync(filePath);
-                List<School>? schools = schoolText.ToObject<JsonNode>()?["schools"]?.ToObject<List<School>>();
-                School? school = schools?.Find(x => id.Equals(x.id)  && name.Equals(x.name));
-                if (school!=null)
-                {
-                    _liteDBFactory.GetLiteDatabase().GetCollection<School>().DeleteAll();
-                    _liteDBFactory.GetLiteDatabase().GetCollection<School>().Upsert(school);
-                    IEnumerable<School> schoolsDb = _liteDBFactory.GetLiteDatabase().GetCollection<School>().FindAll();
-                    School? schoolDb = schools?.FirstOrDefault();
-                    _memoryCache.TryGetValue(Constant._KeyServerDevice, out ServerDevice? server);
-                    if (server!=null)
-                    {
-                        server.school = school;
-                    }
-                    string ip = GetIP();
-                    var device = IndexService.GetDeviceInit(HttpContext, $"{fp}", ip, _memoryCache);
-                    int hybrid = 0;
-                    _memoryCache.Set(Constant._KeyServerDevice, server);
-                    _memoryCache.TryGetValue(Constant._KeyServerCenter, out JsonNode? data);
-                    _memoryCache.TryGetValue(Constant._KeyServerDevice, out server);
-                    if (data!=null)
-                    {
-                        hybrid=1;
-                        msg="云端服务连接成功!";
-                        return Ok(new { code = 200, msg, data = new { hybrid, device, centerUrl = data["centerUrl"], region = data["region"], ip = data["ip"], nowtime = DateTimeOffset.Now.ToUnixTimeMilliseconds(), server } });
-                    }
-                    else
-                    {
-                        msg="云端服务未连接!";
-                        return Ok(new { code = 200, msg, data = new { hybrid, device, centerUrl = "", region = "局域网·内网", ip = ip, nowtime = DateTimeOffset.Now.ToUnixTimeMilliseconds(), server } });
-                    }
-                }
-                else
-                {
-                    return Ok(new { code = 400, msg = "绑定失败!" });
-                }
-            }
-            else {
-                return Ok(new { code = 400, msg = "参数错误!" });
-            }
-        }
-        [HttpPost("device")]
-        public IActionResult Device(JsonElement json )
-        {
-            try
-            {
-                string ip=   GetIP();
-                json.TryGetProperty("fp", out JsonElement fp);
-                var device = IndexService.GetDeviceInit(HttpContext, $"{fp}", ip, _memoryCache);
-                int hybrid = 0, notify = 0 ;
-                _memoryCache.TryGetValue(Constant._KeyServerCenter, out JsonNode? data);
-                _memoryCache.TryGetValue(Constant._KeyServerDevice, out ServerDevice? server);
-                if (_connectionService.notifyIsConnected)
-                { 
-                    notify=1;
-                }
-                if (_connectionService.centerIsConnected)
-                {
-                    hybrid=1;
-                }
-                msg="服务端检测成功!";
-                return Ok(new { code = 200, msg, data = new {
-                    notify,
-                    hybrid,
-                    device,
-                    centerUrl = hybrid==1 ? _connectionService.centerUrl : "",
-                    notifyUrl = notify==1 ? _connectionService.notifyUrl : "",
-                    region = data?["region"],
-                    ip = data!=null ? data?["ip"] : ip,
-                    nowtime = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
-                    server
-                } });
-            }
-            catch (Exception ex)
-            {
-                code=500;
-                msg="服务端异常!";
-            }
-            return Ok(new { code, msg });
-        }
-
-
-        /**
-        {
-            "type":"sms",//qrcode二维码扫码登录:randomCode必传;  sms 短信验证登录:randomCode必传,mobile必传
-            "randomCode",
-            "mobile":"1528377****"
-        }
-        **/
-        /// <summary>
-        /// 登录验证
-        /// </summary>
-        /// <param name="randomCode"></param>
-        /// <returns></returns>
-        [HttpPost("login-check")]
-        public async Task<IActionResult> LoginCheck(JsonNode json)
-        {
-            try
-            {
-                var type = json["type"];
-                string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
-                if (!string.IsNullOrWhiteSpace($"{type}"))
-                {
-                    TmdidImplicit? token = null;
-                    string x_auth_token = string.Empty;
-                    List<School>? schools = null;
-                    JsonNode? jsonNode = null;
-                    long time = DateTimeOffset.Now.ToUnixTimeMilliseconds();
-                    _memoryCache.TryGetValue(Constant._KeyServerDevice, out ServerDevice? server);
-                    School? school = null;
-                    if (server!=null) 
-                    {
-                    school = server.school;
-                    }
-                    switch (true)
-                    {
-                        //跳过忽略,但是仍然要以访客身份登录
-                        case bool when $"{type}".Equals(ExamConstant.ScopeVisitor):
-                            {
-                                string id = $"{DateTimeOffset.Now.ToUnixTimeSeconds()}";
-                                string name = $"{school?.name}教师-{Random.Shared.Next(100, 999)}";
-                                x_auth_token = JwtAuthExtension.CreateAuthToken("www.teammodel.cn",id ,name,picture: school?.picture
-                                    , ExamConstant.JwtSecretKey,ExamConstant.ScopeVisitor,8,schoolID:school?.id,new string[] { "visitor" }, expire: 1);
-                                //  _memoryCache.Set($"Teacher:{id}", new Teacher { id = id, name = $"{name}", implicit_token = token, picture = null, schools = schools, x_auth_token = x_auth_token });
-                                _liteDBFactory.GetLiteDatabase().GetCollection<Teacher>().Upsert(new Teacher { id = id, name = $"{name}", implicit_token = token, picture = null, schools = schools, x_auth_token = x_auth_token,loginTime=time });
-                                return Ok(new { code = 200,x_auth_token = x_auth_token });
-                            }
-                        case bool when $"{type}".Equals("qrcode"):
-                            {
-                                string randomCode = $"{json["randomCode"]}";
-                                System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
-                                var rc= _memoryCache.Get<string>($"Login:ExamServer:{school?.id}:{randomCode}");
-                                if (!string.IsNullOrWhiteSpace(rc))
-                                {
-                                    var response = await _httpClientFactory.CreateClient().GetAsync($"{CenterUrl}/core/qrcode/check?randomcode={randomCode}&school={school?.id}&client=ExamServer");
-                                    if (response.IsSuccessStatusCode)
-                                    {
-                                        string content = await response.Content.ReadAsStringAsync();
-                                        if (!string.IsNullOrWhiteSpace(content))
-                                        {
-                                            jsonNode = content.ToObject<JsonNode>();
-                                        }
-                                        else
-                                        {
-                                            code=400;
-                                            msg="随机码验证失败";
-                                        }
-                                    }
-                                    else
-                                    {
-                                        code=400;
-                                        msg="随机码验证错误";
-                                    }
-                                }
-                                else
-                                {
-                                    code=400;
-                                    msg="二维码过期";
-                                }
-                                break;
-                            }
-                        case bool when $"{type}".Equals("smspin"):
-                            {
-                                string pin_code = $"{json["pin_code"]}";
-                                string account = $"{json["account"]}";
-                                var response = await _httpClientFactory.CreateClient().PostAsJsonAsync($"{CenterUrl}/core/sendsms/check", new { pin_code, account,school=school?.id });
-                                if (response.IsSuccessStatusCode)
-                                {
-                                    string content = await response.Content.ReadAsStringAsync();
-                                    if (!string.IsNullOrWhiteSpace(content))
-                                    {
-                                        jsonNode = content.ToObject<JsonNode>();
-                                    }
-                                    else
-                                    {
-                                        code=400;
-                                        msg="短信验证返回结果为空";
-                                    }
-                                }
-                                else
-                                {
-                                    code=400;
-                                    msg="短信验证错误";
-                                }
-                                break;
-                            }
-
-                    }
-                    if (jsonNode != null  && $"{jsonNode["code"]}".Equals("200"))
-                    {
-                        token =jsonNode["implicit_token"]?.ToObject<TmdidImplicit>(); 
-                        x_auth_token = $"{jsonNode["x_auth_token"]}";
-                        schools =jsonNode["schools"]?.ToObject<List<School>>(); 
-                        var jwt = new JwtSecurityToken(token?.id_token);
-                        var id = jwt.Payload.Sub;
-                        jwt.Payload.TryGetValue("name", out object? name);
-                        jwt.Payload.TryGetValue("picture", out object? picture);
-                        //_memoryCache.Set($"Teacher:{id}", new Teacher { id=id, name=$"{name}", implicit_token= token, picture=$"{picture}", schools=schools, x_auth_token=x_auth_token });
-                        _liteDBFactory.GetLiteDatabase().GetCollection<Teacher>().Upsert(new Teacher { id=id, name=$"{name}", implicit_token= token, picture=$"{picture}", schools=schools, x_auth_token=x_auth_token ,loginTime=time });
-                        return Ok(new { code=200,/* implicit_token = token, schools = schools , */ x_auth_token = x_auth_token });
-                    }
-                    else
-                    {
-                        code=400;
-                        msg="验证失败";
-                    }
-                }
-                else
-                {
-                    code=400;
-                    msg="参数错误";
-                }
-            }
-            catch (Exception ex)
-            {
-                code=500;
-                msg="异常错误";
-            }
-            return Ok(new { code = code,msg });
-        }
-        /*
-     
-         
-         
-         */
-        /// <summary>
-        /// 登录模式初始化
-        /// </summary>
-        /// <returns></returns>
-        [HttpPost("login-init")]
-        public async Task<IActionResult> LoginInit(JsonNode json)
-        {
-            var type = json["type"];
-            string qrcode = string.Empty;
-            string randomcode = "";
-            _memoryCache.TryGetValue(Constant._KeyServerDevice, out ServerDevice? server);
-            School? school = null;
-            if (server != null)
-            {
-                school = server.school;
-            }
-            if (school != null)
-            {
-                switch (true)
-                {
-                    case bool when $"{type}".Equals("qrcode"):
-                        {
-                            //.NET Core使用SkiaSharp快速生成二维码  https://cloud.tencent.com/developer/article/2336486
-
-                            // 生成二维码图片
-                            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";
-                            var str = QRCodeHelper.GenerateQRCode(content, 300, 300, QRCodeHelper.logo);
-                            qrcode = $"data:image/png;base64,{str}";
-                            int ttl = 60;
-                            string  key = $"Login:ExamServer:{school?.id}:{randomcode}";
-                            _memoryCache.Set(key, randomcode, TimeSpan.FromSeconds(ttl));
-                            var device =IndexService.GetDevice(HttpContext,_memoryCache);
-
-                            _memoryCache.Set($"device:{key}", device);
-                            try {
-                                if (_connectionService.notifyIsConnected && _connectionService.serverDevice!=null) 
-                                {
-                                    string url = $"{_connectionService.notifyUrl!}/third/ies/qrcode-login-register";
-                                    //扫描登录,远端设备登记临时随机码
-                                    await _httpClientFactory.CreateClient().PostAsJsonAsync(url,new { randomcode= key, deviceid = _connectionService .serverDevice.deviceId});
-                                }
-                            } catch (Exception e) {
-                                _logger.LogError($"{e.Message},{e.StackTrace}");
-                            }
-                            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;
-                            if (!string.IsNullOrWhiteSpace($"{json["area"]}") && !string.IsNullOrWhiteSpace($"{json["to"]}"))
-                            {
-                                string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
-                                string url = $"{CenterUrl}/core/sendsms/pin";
-                                HttpResponseMessage message = await _httpClientFactory.CreateClient().PostAsJsonAsync(url, new { area = json["area"], to = json["to"], lang = "zh-cn" });
-                                if (message.IsSuccessStatusCode)
-                                {
-                                    string content = await message.Content.ReadAsStringAsync();
-                                    JsonNode? jsonNode = content?.ToObject<JsonNode>();
-                                    if (jsonNode != null && int.TryParse($"{jsonNode["send"]}", out int s))
-                                    {
-                                        send = s;
-                                    }
-                                }
-                            }
-                            return Ok(new { code = 200, send, type });
-                        }
-                }
-            }
-            else if (school == null) 
-            {
-                return Ok(new { code = 400,msg="未绑定学校" });
-            }
-            return Ok(new { code = 400 });
-        }
-
-    }
-}

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

@@ -1,651 +0,0 @@
-using IES.ExamLib.Models;
-using IES.ExamServer.DI;
-using IES.ExamServer.DI.SignalRHost;
-using IES.ExamServer.Filters;
-using IES.ExamServer.Helper;
-using IES.ExamServer.Helpers;
-using IES.ExamServer.Models;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.SignalR;
-using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Extensions.Configuration;
-using System.Linq.Expressions;
-using System.Net.Http;
-using System.Net.Http.Json;
-using System.Text.Json;
-using System.Text.Json.Nodes;
-
-namespace IES.ExamServer.Controllers
-{
-    [ApiController]
-    [Route("manage")]
-    public class ManageController:BaseController
-    {
-        private readonly IConfiguration _configuration;
-        private readonly IHttpClientFactory _httpClientFactory;
-        private readonly IMemoryCache _memoryCache;
-        private readonly ILogger<ManageController> _logger;
-        private readonly LiteDBFactory _liteDBFactory;
-        private readonly CenterServiceConnectionService _connectionService;
-        private readonly int DelayMicro = 10;//微观数据延迟
-        private readonly int DelayMacro = 100;//宏观数据延迟
-        private readonly IHubContext<SignalRExamServerHub> _signalRExamServerHub;
-        public ManageController(LiteDBFactory liteDBFactory,ILogger<ManageController> logger, IConfiguration configuration,
-            IHttpClientFactory httpClientFactory, IMemoryCache memoryCache, CenterServiceConnectionService connectionService, IHubContext<SignalRExamServerHub> signalRExamServerHub)
-        {
-            _logger = logger;
-            _configuration=configuration;
-            _httpClientFactory=httpClientFactory;
-            _memoryCache=memoryCache;
-            _liteDBFactory=liteDBFactory;
-            _connectionService=connectionService;
-            _signalRExamServerHub=signalRExamServerHub;
-        }
-        ///通过线上回传数据需要鉴权验证等操作。
-        ///通过离线包回传数据需要加密操作
-        
-        /// <summary>
-        /// 清理缓存,列出缓存占用空间,type =list列出,type=clear清理,不能清理近期及正在激活的数据,并且提示清理中暂未上传或者导出的数据。
-        /// </summary>
-        /// <param name="json"></param>
-        /// <returns></returns>
-        [HttpPost("clean-cache")]
-        [AuthToken("admin", "teacher", "visitor")]
-        public async Task<IActionResult> CleanCache(JsonNode json) 
-        {
-            return Ok();
-        }
-        [HttpPost("download-package")]
-        [AuthToken("admin","teacher")]
-        public async Task<IActionResult> DownloadPackage(JsonNode json)
-        {
-            //C#.NET 6 后端与前端流式通信
-            //https://www.doubao.com/chat/collection/687687510791426?type=Thread
-            //下载日志记录:1.步骤,检查,2.获取描述信息,3.分类型,4下载文件,5.前端处理,6.返回结果 , 正在下载...==> [INFO]https://www.doubao.com/chat/collection/687687510791426?type=Thread [Size=180kb] Ok...
-            //进度条 展示下载文件总大小和已下载,末尾展示 文件总个数和已下载个数
-            //https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js
-            /* int data = 0,blob=0,webview=0, groupList=0
-             {
-                "evaluationId":"idssss",
-                "shortCode":"1234567890",
-                "ownerId":"hbcn/tmdid",
-                "data":1,
-                "blob":1,
-                "webview":1,
-                "groupList":1
-            }
-             */
-            //如果要访问中心,则需要教师登录联网。  
-            var token = GetAuthTokenInfo();
-            if (token.scope.Equals(ExamConstant.ScopeTeacher))
-            {
-                if (_connectionService.centerIsConnected) 
-                {
-                    Teacher? teacher = _liteDBFactory.GetLiteDatabase().GetCollection<Teacher>().FindOne(x => x.id!.Equals(token.id));
-                    if (teacher != null)
-                    {
-                        string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
-                        var client = _httpClientFactory.CreateClient();
-                        if (client.DefaultRequestHeaders.Contains(Constant._X_Auth_AuthToken))
-                        {
-                            client.DefaultRequestHeaders.Remove(Constant._X_Auth_AuthToken);
-                        }
-                        client.DefaultRequestHeaders.Add(Constant._X_Auth_AuthToken, teacher.x_auth_token);
-                        HttpResponseMessage message = await client.PostAsJsonAsync($"{CenterUrl}/blob/sas-read", new { containerName = $"{json["ownerId"]}" });
-                        string sas = string.Empty;
-                        string url = string.Empty;
-                        string cnt = string.Empty;
-                        if (message.IsSuccessStatusCode)
-                        {
-                            //url  sas timeout name
-                            string content = await message.Content.ReadAsStringAsync();
-                            JsonNode? jsonNode = content.ToObject<JsonNode>();
-                            if (jsonNode != null)
-                            {
-                                sas = $"{jsonNode["sas"]}";
-                                cnt = $"{jsonNode["name"]}";
-                            }
-                        }
-                        var httpClient= _httpClientFactory.CreateClient();
-                        if ($"{json["data"]}".Equals("1")) 
-                        {
-                           await httpClient.GetAsync($"{url}/{cnt}/exam/{json["evaluationId"]}/package/evaluation.json");
-                        }
-                        if ($"{json["groupList"]}".Equals("1"))
-                        {
-                            await httpClient.GetAsync($"{url}/{cnt}/exam/{json["evaluationId"]}/package/grouplist.json");
-                        }
-                        if ($"{json["blob"]}".Equals("1"))
-                        {
-                            await httpClient.GetAsync($"{url}/{cnt}/exam/{json["evaluationId"]}/paper");
-                        }
-                        if ($"{json["webview"]}".Equals("1"))
-                        {
-                            //await httpClient.GetAsync($"{url}/{cnt}/exam/{json["evaluationId"]}/evaluation.json");
-                        }
-                    }
-                }
-            }
-           return Ok();
-        }
-        [HttpPost("check-short-code")]
-        [AuthToken("admin", "teacher", "visitor")]
-        public async Task<IActionResult> CheckShortCode(JsonNode json)
-        {
-           
-            string shortCode = $"{json["shortCode"]}";
-            string evaluationId = $"{json["evaluationId"]}";
-            string deviceId = $"{json["deviceId"]}";
-            string centerCode= string.Empty, centerMsg= string.Empty;
-            Expression<Func<EvaluationClient, bool>> predicate = x => true;
-            int checkTotal = 0, checkSuccess = 0, checkError = 0, checkWarning = 0;
-            int msg_status = Constant._Message_status_info;
-            if (!string.IsNullOrEmpty(shortCode))
-            {
-                var codePredicate = ExpressionHelper.Or<EvaluationClient>(
-                    x => !string.IsNullOrWhiteSpace(x.shortCode) &&  x.shortCode == shortCode,
-                    x => !string.IsNullOrWhiteSpace(x.password) &&  x.password == shortCode
-                );
-                predicate= predicate.And(codePredicate);
-            }
-            else {
-                return Ok(new { code = 400,msg="必须输入开卷码" });
-            }
-            if (!string.IsNullOrWhiteSpace(evaluationId))
-            {
-                predicate= predicate.And(x => x.id!.Equals(evaluationId));
-            }
-            IEnumerable<EvaluationClient>? evaluationClients = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Find(predicate);
-            EvaluationClient? evaluationLocal = null;
-            EvaluationClient? evaluationCloud = null;
-            if (evaluationClients!=null && evaluationClients.Count()>0)
-            {
-                evaluationLocal= evaluationClients.First();
-            }
-            //如果要访问中心,则需要教师登录联网。  
-            var token = GetAuthTokenInfo();
-            if (token.scope.Equals(ExamConstant.ScopeTeacher))
-            {
-                if (_connectionService.centerIsConnected)
-                {
-                    Teacher? teacher = _liteDBFactory.GetLiteDatabase().GetCollection<Teacher>().FindOne(x => x.id!.Equals(token.id));
-                    if (teacher != null)
-                    {
-                        string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
-                        var client = _httpClientFactory.CreateClient();
-                        if (client.DefaultRequestHeaders.Contains(Constant._X_Auth_AuthToken))
-                        {
-                            client.DefaultRequestHeaders.Remove(Constant._X_Auth_AuthToken);
-                        }
-                        client.DefaultRequestHeaders.Add(Constant._X_Auth_AuthToken, teacher.x_auth_token);
-                        try {
-                            HttpResponseMessage message = await client.PostAsJsonAsync($"{CenterUrl}/evaluation-sync/find-sync-info", new { shortCode, evaluationId });
-                            if (message.IsSuccessStatusCode)
-                            {
-                                string content = await message.Content.ReadAsStringAsync();
-                                JsonNode? jsonNode = content.ToObject<JsonNode>();
-                                if (jsonNode != null)
-                                {
-                                    centerCode = $"{jsonNode["code"]}";
-                                    centerMsg = $"{jsonNode["msg"]}";
-                                    if ($"{jsonNode["code"]}".Equals("200"))
-                                    {
-                                        evaluationCloud = jsonNode["evaluation"]?.ToObject<EvaluationClient>();
-                                    }
-                                }
-                                else
-                                {
-                                    centerCode = "500";
-                                    centerMsg = "数据转换异常";
-                                }
-                            }
-                            else
-                            {
-                                centerCode = $"{message.StatusCode}";
-                                centerMsg = "数据中心访问异常";
-                            }
-                        } catch (Exception ex) {
-                            centerCode = $"500";
-                            centerMsg = $"数据中心访问异常:{ex.Message}";
-                        }
-                    }
-                    else
-                    {
-                        centerCode = $"401";
-                        centerMsg = "当前登录账号未找到";
-                    }
-                }
-                else 
-                {
-                    centerCode = $"404";
-                    centerMsg = "云端数据中心未连接";
-                }
-                if (centerCode.Equals("200"))
-                {
-                    msg_status=Constant._Message_status_success;
-                    checkTotal++;
-                    checkSuccess++;
-                }
-                else 
-                {
-                    msg_status=Constant._Message_status_warning;
-                    checkTotal++;
-                    checkWarning++;
-                }
-                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                               new MessageContent { dataId=evaluationCloud?.id, dataName=evaluationCloud?.name, messageType=Constant._Message_type_message, status=msg_status, content=$"云端数据检测结果:{centerMsg},状态:{centerCode}" });
-            }
-
-            //数据,文件,页面 0 没有更新,1 有更新
-            int data = 0,blob=0,webview=0, groupList=0, status=0;
-            long dataSize = 0, blobSize=0 , webviewSize=0, studentCount=0;
-            if (evaluationLocal== null && evaluationCloud==null)
-            {
-                //线上线下没有数据
-                status=1;
-                
-            }
-            else if (evaluationLocal!=null && evaluationCloud!=null) 
-            {
-                //线上线下有数据
-                status = 2;
-                if ((!string.IsNullOrWhiteSpace(evaluationLocal.blobHash) &&  !evaluationLocal.blobHash.Equals(evaluationCloud.blobHash))
-                    ||(evaluationLocal.blobTime<evaluationCloud.blobTime) 
-                    ||(evaluationLocal.blobCount!= evaluationCloud.blobCount)
-                    ||(evaluationLocal.blobSize!= evaluationCloud.blobSize))
-                {
-                    blob=1;
-                    blobSize=evaluationCloud.blobSize;
-                }
-                if ((evaluationLocal.dataTime<evaluationCloud.dataTime)
-                    ||(evaluationLocal.dataSize!=evaluationCloud.dataSize)
-                    ||(evaluationLocal.paperCount!= evaluationCloud.paperCount)
-                    )
-                {
-                    data=1;
-                    dataSize=evaluationCloud.dataSize;
-                }
-                if ((evaluationLocal.webviewCount!=evaluationCloud.webviewCount)
-                    ||(evaluationLocal.webviewSize!= evaluationCloud.webviewSize)
-                    ||(evaluationLocal.webviewTime!= evaluationCloud.webviewTime)
-                    ||(!string.IsNullOrWhiteSpace(evaluationLocal.webviewPath)&&  !evaluationLocal.webviewPath.Equals(evaluationCloud.webviewPath)))
-                {
-                    webview=1;
-                    webviewSize=evaluationCloud.webviewSize;
-                }
-                if ((evaluationLocal.studentCount!= evaluationCloud.studentCount)||(!$"{evaluationLocal.grouplistHash}".Equals(evaluationCloud.grouplistHash)))  
-                {
-                    groupList=1;
-                    studentCount=evaluationCloud.studentCount;
-                }
-            }
-            else if (evaluationLocal!=null && evaluationCloud==null)
-            {
-                //线下有数据,线上没有数据,可能没联网。
-                status = 3;
-            }
-            else if (evaluationLocal==null && evaluationCloud!=null)
-            {
-                //线下没有数据,线上有数据
-                evaluationLocal= evaluationCloud;
-                blob=1;
-                data=1;
-                webview=1;
-                groupList=1;
-                blobSize=evaluationCloud.blobSize;
-                dataSize=evaluationCloud.dataSize;
-                webviewSize=evaluationCloud.webviewSize;
-                studentCount=evaluationCloud.studentCount;
-                status = 4;
-                _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Insert(evaluationLocal);
-            }
-            
-            if (evaluationLocal!=null)
-            {
-                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file, 
-                    new MessageContent {dataId=evaluationLocal.id,dataName=evaluationLocal.name,messageType=Constant._Message_type_message, status=0, content="开始检查评测信息文件.." });
-                 
-                //校验本地文件数据
-                string packagePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package");
-                if (!Directory.Exists(packagePath))
-                {
-                    Directory.CreateDirectory(packagePath);
-                }
-                string evaluationPath = Path.Combine(packagePath, evaluationLocal.id!);
-                string evaluationDataPath = Path.Combine(evaluationPath,"data");
-                // await Task.Delay(DelayMacro);
-
-                //await Task.Delay(DelayMacro);
-                string path_groupList = Path.Combine(evaluationDataPath, "groupList.json");
-                msg_status =Constant._Message_status_info;
-                if (!System.IO.File.Exists(path_groupList))
-                {
-                    groupList=1;
-                    msg_status=Constant._Message_status_error;
-                    checkTotal++;
-                    checkError++;
-                }
-                else
-                {
-                    msg_status=Constant._Message_status_success;
-                    checkTotal++;
-                    checkSuccess++;
-                }
-                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file, 
-                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content=$"评测名单文件:{path_groupList}" });
-                //await Task.Delay(DelayMacro);
-                string path_source = Path.Combine(evaluationDataPath, "source.json");
-                msg_status = Constant._Message_status_info;
-                if (!System.IO.File.Exists(path_source))
-                {
-                    data=1;
-                    msg_status=Constant._Message_status_error;
-                    checkTotal++;
-                    checkError++;
-                }
-                else
-                {
-                    msg_status=Constant._Message_status_success;
-                    checkTotal++;
-                    checkSuccess++;
-                }
-                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content=$"评测原始数据:{path_source}" });
-                msg_status =Constant._Message_status_info;
-                try {
-                    //TODO 重整本地文件路径。 文件可能不存在D:\VisualStudioProjects\TEAMModelOS\TEAMModelOS.Extension\IES.Exam\IES.ExamServer\wwwroot\package\exam\6af32bbd-144e-4366-8bc0-61ba4c85677c\evaluation.json
-                    string path_evaluation = Path.Combine(evaluationDataPath, "evaluation.json");
-                    if (!System.IO.File.Exists(path_evaluation))
-                    {
-                        blob=1;
-                        data=1;
-                        msg_status=Constant._Message_status_error;
-                        checkTotal++;
-                        checkError++;
-                    }
-                    else
-                    {
-                        msg_status=Constant._Message_status_success;
-                        checkTotal++;
-                        checkSuccess++;
-                    }
-                    //数据格式:  [消息][信息/错误/警告][15:43]=>[开始检查评测信息文件...]
-                    //数据格式:  [检查][成功/失败][15:43]=>[评测数据文件:/wwwroot/package/623a9fe6-5445-0938-ff77-aeb80066ef27/evaluation.json]
-                    //数据格式:  [下载][成功/失败][15:43]=>[评测数据文件:/wwwroot/package/623a9fe6-5445-0938-ff77-aeb80066ef27/evaluation.json][1024kb][15ms]
-                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                        new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content=$"评测数据文件:{path_evaluation}" });
-                    if (System.IO.File.Exists(path_evaluation)) 
-                    {
-                        string evaluation_str = await System.IO.File.ReadAllTextAsync(path_evaluation);
-                        JsonNode? evaluation_data = evaluation_str.ToObject<JsonNode>();
-                        if (evaluation_data==null)
-                        {
-                            blob=1;
-                            data=1;
-                        }
-                        else
-                        {
-                            EvaluationClient? evaluationClient = evaluation_data["evaluationClient"]?.ToObject<EvaluationClient>();
-                            if (evaluationClient!=null)
-                            {
-                                if ((!string.IsNullOrWhiteSpace(evaluationLocal.blobHash) && evaluationLocal.blobHash.Equals(evaluationClient.blobHash))
-                                    &&(evaluationLocal.blobTime==evaluationClient.blobTime)
-                                    &&(evaluationLocal.blobCount== evaluationClient.blobCount)
-                                    &&(evaluationLocal.blobSize== evaluationClient.blobSize)&& (evaluationLocal.dataTime==evaluationClient.dataTime)
-                                    &&(evaluationLocal.dataSize==evaluationClient.dataSize)&&(evaluationLocal.webviewCount==evaluationClient.webviewCount)
-                                    &&(evaluationLocal.webviewSize== evaluationClient.webviewSize)
-                                    &&(evaluationLocal.webviewTime== evaluationClient.webviewTime)
-                                    &&(!string.IsNullOrWhiteSpace(evaluationLocal.webviewPath)&&  evaluationLocal.webviewPath.Equals(evaluationClient.webviewPath)))
-                                {
-                                    msg_status=Constant._Message_status_info;
-                                }
-                                else
-                                {
-                                    data=1;
-                                    msg_status=Constant._Message_status_error;
-                                }
-                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="校验本地试卷文件..." });
-                            }
-                            else { data=1; }
-
-                            List<EvaluationExam>? evaluationExams = evaluation_data["evaluationExams"]?.ToObject<List<EvaluationExam>>();
-                            if (evaluationExams.IsEmpty())
-                            {
-                                blob=1;
-                                data=1;
-                            }
-                            else 
-                            {
-                                
-                                foreach (var evaluationExam in evaluationExams!)
-                                {
-                                    string path_papers = Path.Combine(evaluationPath,  "exams");
-                                    var papers_files = FileHelper.ListAllFiles(path_papers);
-                                    int paperIndex = 0;
-                                    foreach (var paper in evaluationExam.papers)
-                                    {
-                                        paperIndex++;
-                                        List<MessageContent> contents = new List<MessageContent>();
-                                        int paper_error_count = 0;
-                                        foreach (var blobInfo in paper.blobs)
-                                        {
-                                            msg_status=Constant._Message_status_info;
-                                            if (!string.IsNullOrWhiteSpace(blobInfo.path))
-                                            {
-
-                                                var file = papers_files.Find(x => x.Contains(blobInfo.path));
-                                                if (file!=null)
-                                                {
-                                                    msg_status=1;
-                                                    msg_status=Constant._Message_status_success;
-                                                }
-                                                else
-                                                {
-                                                    msg_status=Constant._Message_status_error;
-                                                    paper_error_count++;
-                                                }
-
-                                            }
-                                            else
-                                            {
-                                                msg_status=Constant._Message_status_warning; ;
-                                                paper_error_count++;
-                                            }
-                                            contents.Add(new MessageContent
-                                            {
-                                                dataId=evaluationLocal.id,
-                                                dataName=evaluationLocal.name,
-                                                messageType=Constant._Message_type_check,
-                                                status=msg_status,
-                                                content=$"试卷文件信息:{paper.paperName}"
-                                            });
-                                        }
-                                        int paper_msg_status = Constant._Message_status_info;
-                                        if (paper_error_count>0)
-                                        {
-                                            blob=1;
-                                            paper_msg_status=Constant._Message_status_error;
-                                        }
-                                        else
-                                        {
-                                            paper_msg_status=Constant._Message_status_success;
-                                        }
-                                        await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                                            new MessageContent
-                                            {
-                                                dataId=evaluationLocal.id,
-                                                dataName=evaluationLocal.name,
-                                                messageType=Constant._Message_type_message,
-                                                status=paper_msg_status,
-                                                content=$"试卷名称:[{paperIndex}]{evaluationExam.examName}-{evaluationExam.subjectName}-{paper.paperName}\r\n文件数量:{paper.blobs.Count()},检测成功数量:{contents.Count(x => x.status==Constant._Message_status_success)},检测异常数量{contents.Count(x => x.status==Constant._Message_status_error)}",
-                                                contents=contents
-                                            });
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-                catch (Exception e) {
-                    _logger.LogData<object>(new {code=500,msg=e.Message,data = new { content= e.StackTrace } }, evaluationLocal.id!);
-                }
-
-                //检查需要更新的项目:
-                if (data==1) 
-                {
-                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                        new MessageContent
-                        {
-                            dataId=evaluationLocal.id,
-                            dataName=evaluationLocal.name,
-                            messageType=Constant._Message_type_message,
-                            status=Constant._Message_status_warning,
-                            content=$"检查到评测数据需要更新。[{dataSize}]"
-                        });
-                }
-                if (blob==1)
-                {
-                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                        new MessageContent
-                        {
-                            dataId=evaluationLocal.id,
-                            dataName=evaluationLocal.name,
-                            messageType=Constant._Message_type_message,
-                            status=Constant._Message_status_warning,
-                            content=$"检查到评测试卷需要更新。[{blobSize}]"
-                        });
-                }
-                if (webview==1) 
-                {
-                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                        new MessageContent
-                        {
-                            dataId=evaluationLocal.id,
-                            dataName=evaluationLocal.name,
-                            messageType=Constant._Message_type_message,
-                            status=Constant._Message_status_warning,
-                            content=$"检查到评测作答页面需要更新。[{webviewSize}]"
-                        });
-                }
-                if (groupList==1) 
-                {
-                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                        new MessageContent
-                        {
-                            dataId=evaluationLocal.id,
-                            dataName=evaluationLocal.name,
-                            messageType=Constant._Message_type_message,
-                            status=Constant._Message_status_warning,
-                            content=$"检查到评测名单需要更新。[{studentCount}]"
-                        });
-                }
-            }
-            int finalStatus = Constant._Message_status_success;
-            if (checkWarning>0) 
-            {
-                finalStatus = Constant._Message_status_warning;
-            }
-            if (checkError>0)
-            {
-                finalStatus = Constant._Message_status_error;
-            }
-            await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                new MessageContent {
-                    dataId=evaluationLocal?.id,
-                    dataName=evaluationLocal?.name,
-                    messageType=Constant._Message_type_message,
-                    status=Constant._Message_status_warning,
-                    content=$"最终检测结果:总数({checkTotal}),成功({checkSuccess}),警告({checkWarning}),异常({checkError})。"
-                });
-            return Ok(new {
-                code = 200,
-                evaluation = evaluationLocal,
-                data,
-                blob,
-                webview,
-                dataSize,
-                blobSize,
-                webviewSize,
-                status,
-                groupList,
-                studentCount,
-                checkTotal,
-                checkSuccess,
-                checkError,
-                checkWarning,
-            });
-        }
-
-        /// <summary>
-        /// 激活或者取消激活考试
-        /// </summary>
-        /// <param name="json"></param>
-        /// <returns></returns>
-        [HttpPost("activate-evaluation")]
-        [AuthToken("admin", "teacher", "visitor")]
-        public IActionResult ActivateEvaluation(JsonNode json)
-        {
-            string id = $"{json["id"]}";
-            string shortCode = $"{json["shortCode"]}";
-            string activateStr = $"{json["activate"]}";
-            if (!string.IsNullOrWhiteSpace(id) && !string.IsNullOrWhiteSpace(shortCode)) 
-            {
-                EvaluationClient? evaluationClient = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().FindOne(x => x.id!.Equals(id) && !string.IsNullOrWhiteSpace(x.shortCode) && x.shortCode.Equals(shortCode));
-                if (evaluationClient != null)
-                {
-                    IEnumerable<EvaluationClient> evaluationClients = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Find(x=>x.activate==1);
-                    if (evaluationClients != null && evaluationClients.Count() > 0) 
-                    {
-                        foreach(EvaluationClient item in evaluationClients)
-                        {
-                            item.activate = 0;
-                            _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Upsert(item);
-                        }
-                    }
-                    int activate = 0;
-                    if (int.TryParse(activateStr, out int _activate)) {
-                        activate = _activate;
-                    }
-                    evaluationClient.activate = activate;
-                    _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Upsert(evaluationClient);
-                }
-            }
-            return Ok();
-        }
-        /// <summary>
-        /// 加载本地的活动列表
-        /// </summary>
-        /// <param name="json"></param>
-        /// <returns></returns>
-        [HttpPost("list-local-evaluation")]
-        [AuthToken("admin", "teacher", "visitor")]
-        public IActionResult ListLocalEvaluation(JsonNode json) 
-        {
-
-            IEnumerable<EvaluationClient>? evaluationClients = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().FindAll().OrderByDescending(x=>x.activate).ThenByDescending(x=>x.stime);
-
-            if (evaluationClients != null)
-            {
-                var result = evaluationClients.Select(client =>
-                {
-                    var properties = client.GetType().GetProperties();
-                    var anonymousObject = new Dictionary<string, object?>();
-                    foreach (var property in properties)
-                    {
-                        if (!property.Name.Equals("password") && !property.Name.Equals("shortCode"))
-                        {
-                            anonymousObject[property.Name] = property.GetValue(client);
-                        }
-                    }
-                    return anonymousObject;
-                });
-                return Ok(new { code = 200, evaluation = result });
-            }
-            else {
-                return Ok(new { code = 200, evaluation = new Dictionary<string, object?>() });
-            }
-            
-        }
-    }
-}

+ 0 - 6
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/StudentController.cs

@@ -1,6 +0,0 @@
-namespace IES.ExamServer.Controllers
-{
-    public class StudentController
-    {
-    }
-}

+ 0 - 23
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/CenterServiceConnectionService.cs

@@ -1,23 +0,0 @@
-using IES.ExamServer.Models;
-
-namespace IES.ExamServer.DI
-{
-    public class CenterServiceConnectionService
-    {
-        private bool _centerIsConnected;
-        private bool _notifyIsConnected;
-        public string? centerUrl { get; set; }
-        public string? notifyUrl { get; set; }
-        public ServerDevice? serverDevice {  get; set; }
-        public bool notifyIsConnected
-        {
-            get { return string.IsNullOrWhiteSpace(notifyUrl) ? false : _notifyIsConnected; }
-            set { _notifyIsConnected = value; }
-        }
-        public bool centerIsConnected
-        {
-            get { return string.IsNullOrWhiteSpace(centerUrl) ? false : _centerIsConnected; }
-            set { _centerIsConnected = value; }
-        }
-    }
-}

+ 0 - 248
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/CustomFileLoggerProvider.cs

@@ -1,248 +0,0 @@
-using Microsoft.Extensions.Logging;
-using System;
-using System.Collections.Concurrent;
-using System.Text;
-using System.Text.Encodings.Web;
-using System.Text.Json;
-using System.Text.Unicode;
-using System.Threading.Channels;
-
-namespace IES.ExamServer.DI
-{
-    public class CustomFileLoggerProvider : ILoggerProvider
-    {
-        private readonly string _logDirectory;
-        private readonly bool _enableConsoleOutput;
-        private readonly string _timestamp;
-        private readonly ConcurrentDictionary<string, CustomFileLogger> _loggers = new ConcurrentDictionary<string, CustomFileLogger>();
-
-        public CustomFileLoggerProvider(string logDirectory, bool enableConsoleOutput)
-        {
-            _logDirectory = logDirectory;
-            _enableConsoleOutput = enableConsoleOutput;
-            _timestamp = DateTime.Now.ToString("yyMMddHH"); // 生成时间戳
-            if (!Directory.Exists(_logDirectory))
-            {
-                Directory.CreateDirectory(_logDirectory);
-            }
-        }
-
-        public ILogger CreateLogger(string categoryName)
-        {
-            return _loggers.GetOrAdd(categoryName, name => new CustomFileLogger(_logDirectory, name, _enableConsoleOutput, _timestamp));
-        }
-
-        public void Dispose()
-        {
-            _loggers.Clear();
-        }
-    }
-    public class CustomFileLogger : ILogger
-    {
-        private readonly string _logDirectory;
-        private readonly string _categoryName;
-        private readonly bool _enableConsoleOutput;
-        private static readonly SemaphoreSlim _fileLock = new SemaphoreSlim(1, 1);
-        private readonly List<string> _logBuffer = new List<string>();
-      //  private readonly Timer _flushTimer;
-        private readonly string _timestamp;
-        private readonly Channel<string> _consoleLogChannel;
-        private readonly Channel<string> _fileLogChannel;
-
-        public CustomFileLogger(string logDirectory, string categoryName, bool enableConsoleOutput, string timestamp)
-        {
-            _logDirectory = logDirectory;
-            _categoryName = categoryName;
-            _enableConsoleOutput = enableConsoleOutput;
-            // 每 5 秒刷新一次日志缓冲区
-           // _flushTimer = new Timer(FlushLogs, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
-            _timestamp=timestamp;
-            // 创建无界 Channel
-            _consoleLogChannel = Channel.CreateUnbounded<string>();
-            // 创建无界 Channel
-            _fileLogChannel = Channel.CreateUnbounded<string>();
-            // 启动后台任务处理控制台日志
-            _ = Task.Run(ProcessConsoleLogs);
-            // 启动后台任务处理文件日志
-            _ = Task.Run(ProcessFileLogs);
-        }
-
-        public IDisposable BeginScope<TState>(TState state) => null;
-
-        public bool IsEnabled(LogLevel logLevel) => true;
-
-        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
-        {
-            var logMessage = formatter(state, exception);
-            var logFileName = $"{logLevel}_{_timestamp}.log"; // 按时间戳命名文件
-            var logFilePath = Path.Combine(_logDirectory, logFileName);
-
-            // 异步写入文件
-           // _ = WriteToFileAsync(logFilePath, logLevel, logMessage);
-            // 将日志消息写入 Channel(不阻塞)
-            _fileLogChannel.Writer.TryWrite($"{logFilePath}|||{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{logLevel}] {logMessage}{Environment.NewLine}");
-            // 输出到控制台
-            if (_enableConsoleOutput)
-            {
-                // Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{logLevel}] {logMessage}");
-                // _ = Task.Run(() => Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{logLevel}] {logMessage}")); 
-                _consoleLogChannel.Writer.TryWrite($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{logLevel}] {logMessage}");
-            }
-        }
-        private async Task ProcessConsoleLogs()
-        {
-            await foreach (var logMessage in _consoleLogChannel.Reader.ReadAllAsync())
-            {
-                Console.WriteLine(logMessage); // 输出到控制台
-            }
-        }
-        private async Task ProcessFileLogs()
-        {
-            await foreach (var logEntry in _fileLogChannel.Reader.ReadAllAsync())
-            {
-                try
-                {
-                    // 解析日志条目
-                    var parts = logEntry.Split("|||");
-                    var logFilePath = parts[0];
-                    var logMessage = parts[1];
-
-                    // 异步写入文件
-                    await File.AppendAllTextAsync(logFilePath, logMessage);
-                }
-                catch (Exception ex)
-                {
-                    // 处理文件写入异常
-                    // Console.WriteLine($"Failed to write log to file: {ex.Message}");
-                    _consoleLogChannel.Writer.TryWrite($"Failed to write log to file: {ex.Message}");
-                }
-            }
-        }
-     
-
-        //private async void FlushLogs(object state)
-        //{
-        //    List<string> logsToWrite;
-        //    lock (_logBuffer)
-        //    {
-        //        if (_logBuffer.Count == 0) return;
-        //        logsToWrite = new List<string>(_logBuffer);
-        //        _logBuffer.Clear();
-        //    }
-
-        //    var logFileName = $"{_categoryName}_{_timestamp}.log";
-        //    var logFilePath = Path.Combine(_logDirectory, logFileName);
-
-        //    await _fileLock.WaitAsync(); // 获取锁
-        //    try
-        //    {
-        //        await File.AppendAllLinesAsync(logFilePath, logsToWrite);
-        //    }
-        //    finally
-        //    {
-        //        _fileLock.Release(); // 释放锁
-        //    }
-        //}
-        public void Dispose()
-        {
-           // _flushTimer?.Change(Timeout.Infinite, 0); // 停止定时器
-           // FlushLogs(null); // 最后一次刷新日志
-        }
-    }
-    public static class LoggerExtensions
-    {
-        //private static readonly SemaphoreSlim _dataFileLock = new SemaphoreSlim(1, 1);
-        //private static readonly Channel<string> _fileLogChannel = Channel.CreateUnbounded<string>();
-        //private static readonly Channel<string> _consoleLogChannel = Channel.CreateUnbounded<string>();
-        private static readonly Channel<string> _fileLogChannel = Channel.CreateBounded<string>(new BoundedChannelOptions(10000)
-        {
-            FullMode = BoundedChannelFullMode.DropWrite // 
-        });
-
-        private static readonly Channel<string> _consoleLogChannel = Channel.CreateBounded<string>(new BoundedChannelOptions(10000)
-        {
-            //DropOldest: 此模式会移除并丢弃通道中最旧的那个数据项,以便为正在写入的新数据腾出空间。通道里最早进入的数据会被舍弃,从而让新的数据可以进入通道。
-            //DropNewest:  在这种模式下,为了给要写入的新数据项腾出空间,会移除并丢弃通道中最新的那个数据项。也就是说,新的数据会覆盖掉原本在通道里最新进入的元素,保证新数据能被写入。
-            //Wait:  当使用这种模式时,如果尝试向已满的有界通道写入数据,调用写入操作的线程会等待,直到通道中有空间可用,然后再完成写入操作。这意味着线程会被阻塞,直到可以成功放入新的数据项。
-            FullMode = BoundedChannelFullMode.DropWrite // 直接丢弃当前正要写入的这个数据项,通道中的内容保持不变,相当于放弃这次写入操作。
-        });
-        //懒加载
-        private static readonly Lazy<Task> _fileLogProcessor = new Lazy<Task>(() => Task.Run(ProcessFileLogs));
-        private static readonly Lazy<Task> _consoleLogProcessor = new Lazy<Task>(() => Task.Run(ProcessConsoleLogs));
-       
-        
-
-        public static void LogData<T>(this ILogger logger, T data, string id)
-        {
-            // 确保后台任务已启动,现在就要用确保启动
-            _ = _fileLogProcessor.Value;
-            _ = _consoleLogProcessor.Value;
-            var logDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Logs", "DataLogs");
-            if (!Directory.Exists(logDirectory))
-            {
-                Directory.CreateDirectory(logDirectory);
-            }
-            if (!string.IsNullOrWhiteSpace(id))
-            {
-                var logFilePath = Path.Combine(logDirectory, $"{id}.log");
-                var logMessage = data?.ToJsonString();
-                _fileLogChannel.Writer.TryWrite($"{logFilePath}|||{DateTime.Now:yyyy-MM-dd HH:mm:ss} [Data] {logMessage}{Environment.NewLine}");
-            }
-            else {
-                _consoleLogChannel.Writer.TryWrite($"日志id为空,日志数据:{data?.ToJsonString()}");
-            }
-            //_ = Task.Run(async () =>
-            //{
-            //    await _dataFileLock.WaitAsync(); // 获取锁
-            //    try
-            //    {
-            //        await File.AppendAllTextAsync(logFilePath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [Data] {logMessage}{Environment.NewLine}");
-            //    }
-            //    finally
-            //    {
-            //        _dataFileLock.Release(); // 释放锁
-            //    }
-            //});
-        }
-        static async Task ProcessFileLogs()
-        {
-            try
-            {
-                await foreach (var logEntry in _fileLogChannel.Reader.ReadAllAsync())
-                {
-                   
-                    try
-                    {
-                        // 解析日志条目
-                        var parts = logEntry.Split("|||");
-                        var logFilePath = parts[0];
-                        var logMessage = parts[1];
-
-                        // 异步写入文件
-                        await File.AppendAllTextAsync(logFilePath, logMessage, Encoding.UTF8);
-                    }
-                    catch (Exception ex)
-                    {
-                        // 处理文件写入异常
-                        // Console.WriteLine($"Failed to write log to file: {ex.Message}");
-                        _consoleLogChannel.Writer.TryWrite($"Failed to write log to file: {ex.Message}");
-                    }
-                }
-            }
-            catch (Exception ex) {
-                _consoleLogChannel.Writer.TryWrite($"Failed to write log to file: {ex.Message}");
-            }
-            
-            // 循环结束后,检查是否有剩余的日志条目
-          
-        }
-         
-        static async Task ProcessConsoleLogs()
-        {
-            await foreach (var logMessage in _consoleLogChannel.Reader.ReadAllAsync())
-            {
-                Console.WriteLine(logMessage); // 输出到控制台
-            }
-        }
-    }
-}

+ 0 - 59
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/LiteDBFactory.cs

@@ -1,59 +0,0 @@
-using LiteDB;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace IES.ExamServer.DI
-{
-    public class LiteDBFactory
-    {
-        private readonly IServiceProvider _services;
-        private readonly IOptionsMonitor<LiteDBFactoryOptions> _optionsMonitor;
-        private readonly ILogger _logger;
-        private ConcurrentDictionary<string, LiteDatabase> LiteDatabases { get; } = new ConcurrentDictionary<string, LiteDatabase>();
-        public LiteDBFactory(IServiceProvider services, IOptionsMonitor<LiteDBFactoryOptions> optionsMonitor, ILogger<LiteDBFactory> logger)
-        {
-            if (services == null) throw new ArgumentNullException(nameof(services));
-            if (optionsMonitor == null) throw new ArgumentNullException(nameof(optionsMonitor));
-
-            _services = services;
-            _optionsMonitor = optionsMonitor;
-            _logger = logger;
-        }
-
-        public LiteDatabase GetLiteDatabase(string name = "Master")
-        {
-            return LiteDatabases.GetOrAdd(name, x => new LiteDatabase(_optionsMonitor.Get(name).Connectionstring));
-        }
-    }
-    public class LiteDBFactoryOptions
-    {
-        public string? Name { get; set; }
-        public string? Connectionstring { get; set; }
-    }
-    public static class LiteDBFactoryExtensions
-    {
-        public static IServiceCollection AddLiteDB(this IServiceCollection services, List<LiteDBFactoryOptions> connectionstrings)
-        {
-            if (services == null) throw new ArgumentNullException(nameof(services));
-            if (connectionstrings == null) throw new ArgumentNullException(nameof(connectionstrings));
-
-
-            services.TryAddSingleton<LiteDBFactory>();
-            //多个连接字符串注入
-            connectionstrings.ForEach(connection =>
-            {
-                services.Configure<LiteDBFactoryOptions>(connection.Name, o => { o.Name = connection.Name; o.Connectionstring = connection.Connectionstring; });
-            });
-
-            return services;
-        }
-    }
-}

+ 0 - 158
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/ServiceInitializer.cs

@@ -1,158 +0,0 @@
-using IES.ExamServer.Services;
-using Microsoft.AspNetCore.Hosting.Server.Features;
-using Microsoft.AspNetCore.Hosting.Server;
-using Microsoft.Extensions.Caching.Memory;
-using System.Text.Encodings.Web;
-using System.Text.Json.Nodes;
-using System.Text.Json;
-using System.Text.Unicode;
-using IES.ExamServer.Helper;
-using IES.ExamServer.Models;
-using System.Security.Policy;
-using IES.ExamServer.Helpers;
-
-namespace IES.ExamServer.DI
-{
-    public class ServiceInitializer
-    {
-        private readonly IMemoryCache _cache;
-        private readonly IHttpClientFactory _clientFactory;
-        private readonly LiteDBFactory _liteDBFactory;
-        private readonly IConfiguration _configuration;
-        private readonly CenterServiceConnectionService _connectionService;
-        private readonly IHostApplicationLifetime _lifetime;
-        private readonly IServer _server;
-        private readonly ILogger<ServiceInitializer> _logger;
-
-        public ServiceInitializer(IMemoryCache cache,
-        IHttpClientFactory clientFactory,
-        LiteDBFactory liteDBFactory,
-        IConfiguration configuration,
-        CenterServiceConnectionService connectionService,
-        IHostApplicationLifetime lifetime,
-        IServer server,
-        ILogger<ServiceInitializer> logger)
-        {
-            _cache = cache;
-            _clientFactory = clientFactory;
-            _liteDBFactory = liteDBFactory;
-            _configuration = configuration;
-            _connectionService = connectionService;
-            _lifetime = lifetime;
-            _server = server;
-            _logger = logger;
-        }
-
-        public async Task InitializeAsync()
-        {
-            JsonNode? data = null;
-            int hybrid = 0, notify=0;
-            string remote = "127.0.0.1";
-            string region = "局域网·内网";
-            string? centerUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
-            try
-            {
-                
-                var httpClient = _clientFactory.CreateClient();
-                httpClient.Timeout = TimeSpan.FromSeconds(10);
-                HttpResponseMessage message = await httpClient.PostAsJsonAsync($"{centerUrl}/core/system-info", new { });
-
-                if (message.IsSuccessStatusCode)
-                {
-                    string content = await message.Content.ReadAsStringAsync();
-                    data = JsonSerializer.Deserialize<JsonNode>(content);
-                    data!["centerUrl"] = centerUrl;
-                    _cache.Set(Constant._KeyServerCenter, data);
-                    remote = $"{data["ip"]}";
-                    region = $"{data["region"]}";
-                    hybrid = 1;
-                }
-            }
-            catch (Exception ex)
-            {
-                // 云端服务连接失败
-                hybrid = 0;
-            }
-            string? notifyUrl = _configuration.GetValue<string>("ExamServer:NotifyUrl");
-            try
-            {
-                
-                var httpClient = _clientFactory.CreateClient();
-                httpClient.Timeout = TimeSpan.FromSeconds(10);
-                HttpResponseMessage message = await httpClient.PostAsJsonAsync($"{notifyUrl}/index/device-init", new { fp= Guid.NewGuid().ToString() });
-                if (message.IsSuccessStatusCode)
-                {
-                    notify = 1;
-                }
-            }
-            catch (Exception ex)
-            {
-                // 云端服务连接失败
-                notify = 0;
-            }
-            if (hybrid==1)
-            {
-              
-                var httpClient = _clientFactory.CreateClient();
-                httpClient.Timeout = TimeSpan.FromSeconds(10);
-                HttpResponseMessage message = await httpClient.GetAsync("https://teammodelos.blob.core.chinacloudapi.cn/0-public/schools.json");
-                if (message.IsSuccessStatusCode)
-                {
-                    // 读取响应内容
-                    string content = await message.Content.ReadAsStringAsync();
-                    // 保存文件的路径
-                    string filePath = Path.Combine(Directory.GetCurrentDirectory(),"wwwroot", "package", "schools.json");
-                    // 确保目录存在
-                    Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
-                    // 将内容写入文件
-                    await File.WriteAllTextAsync(filePath, content);
-                }
-                else
-                {
-                    throw new Exception($"Failed to download data. Status code: {message.StatusCode}");
-                }
-            }
-            _connectionService.notifyUrl = notify == 1 ? notifyUrl : null;
-            _connectionService.notifyIsConnected = notify == 1;
-            // 单例模式存储云端数据中心连接状态
-            _connectionService.centerUrl = hybrid == 1 ?centerUrl : null;
-            _connectionService.centerIsConnected = hybrid == 1;
-            ServerDevice serverDevice = IndexService.GetServerDevice(remote, region);
-            IEnumerable<School> schools = _liteDBFactory.GetLiteDatabase().GetCollection<School>().FindAll();
-            School? school = schools?.FirstOrDefault();
-            serverDevice.school = school;
-            _cache.Set(Constant._KeyServerDevice, serverDevice);
-           
-            _liteDBFactory.GetLiteDatabase().GetCollection<ServerDevice>().Upsert(serverDevice);
-            _connectionService.serverDevice = serverDevice;
-            _lifetime.ApplicationStarted.Register(() =>
-            {
-               var serverDevice=  _cache.Get<ServerDevice>(Constant._KeyServerDevice);
-                var _url = _server.Features.Get<IServerAddressesFeature>()?.Addresses;
-                if (_url!.IsNotEmpty())
-                {
-                    List<UriInfo> ports = new List<UriInfo>();
-                    foreach (var url in _url!)
-                    {
-                        Uri uri = new Uri(url);
-                        serverDevice.uris.Add(new UriInfo { port= uri.Port, protocol= uri.Scheme });
-                    }
-
-                }
-                else
-                {
-                    throw new Exception("未获取到端口信息!");
-                }
-                _logger.LogInformation($"服务端设备信息:{JsonSerializer.Serialize(serverDevice, options: new JsonSerializerOptions { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) })}");
-                _cache.Set(Constant._KeyServerDevice, serverDevice);
-            });
-
-            // 退出程序
-            _lifetime.ApplicationStopping.Register(() =>
-            {
-                Console.WriteLine("The application is stopping. Performing cleanup...");
-                // 在这里添加清理资源、保存数据等逻辑
-            });
-        }
-    }
-}

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

@@ -1,143 +0,0 @@
-using IES.ExamServer.DI.SignalRHost;
-using IES.ExamServer.Helper;
-using IES.ExamServer.Models;
-using Microsoft.AspNetCore.Hosting.Server;
-using Microsoft.AspNetCore.Hosting.Server.Features;
-using Microsoft.AspNetCore.SignalR;
-using Microsoft.AspNetCore.SignalR.Client;
-using Microsoft.Extensions.Caching.Memory;
-using System.Text;
-using System.Text.Json.Nodes;
-using System.Web;
-
-namespace IES.ExamServer.DI
-{
-    public class SignalRCloudClientHub : BackgroundService, IDisposable
-    {
-        private readonly IConfiguration _configuration;
-        private readonly ILogger<SignalRCloudClientHub> _logger;
-        private readonly IHttpClientFactory _httpClientFactory;
-        private readonly IMemoryCache _memoryCache;
-        private readonly IHubContext<SignalRExamServerHub> _signalRExamServerHub;
-        public SignalRCloudClientHub(IConfiguration configuration, ILogger<SignalRCloudClientHub> logger, IHttpClientFactory httpClientFactory, IMemoryCache memoryCache,
-            IHubContext<SignalRExamServerHub> signalRExamServerHub)
-        {
-
-            _configuration=configuration;
-            _logger=logger;
-            _httpClientFactory=httpClientFactory;
-            _memoryCache=memoryCache;
-            _signalRExamServerHub=signalRExamServerHub;
-        }
-        protected async override Task ExecuteAsync(CancellationToken stoppingToken)
-        {
-            _memoryCache.TryGetValue(Constant._KeyServerDevice, out ServerDevice? server);
-            string? deviceId = server?.deviceId;
-            string? NotifyUrl = _configuration.GetValue<string>("ExamServer:NotifyUrl");
-            await StartHubConnectionAsync(deviceId, NotifyUrl);
-
-        }
-        private async Task StartHubConnectionAsync(string? deviceId,string? NotifyUrl)
-        {
-            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
-               .WithAutomaticReconnect(reconnectPolicy)
-               .ConfigureLogging(logging =>
-               {
-                   logging.SetMinimumLevel(LogLevel.Information);
-                   logging.AddConsole();
-               })
-               .Build();
-            try
-            {
-                hubConnection.On<ConnectionMessageBody>("ReceiveConnection", (message) =>
-                {
-                    _logger.LogInformation($"连接成功:{message.ToJsonString()}");
-                    //重置重连次数。
-                    reconnectPolicy.Reset();
-                });
-                hubConnection.On<ConnectionMessageBody>("ReceiveMessage", async (message) =>
-                {
-                    if ($"{Constant._Message_grant_type_ies_qrcode_login}" .Equals(message?.grant_type))
-                    {
-                        string deviceId = string.Empty;
-                        var nodeData= message?.content?.ToObject<JsonNode>();
-                        if (nodeData!=null)
-                        {
-                            string randomcode = $"{nodeData["randomcode"]}";
-                            deviceId=_memoryCache.Get<string>($"device:{randomcode}");
-                        }
-                        await  _signalRExamServerHub.SendMessage(_memoryCache,_logger, deviceId, Constant._Message_grant_type_ies_qrcode_login,
-                          new MessageContent {dataId=deviceId,dataName="",messageType =Constant._Message_type_message, status=1,content=$"{nodeData?.ToJsonString()}"});
-                    }
-                    else
-                    {
-                        _logger.LogInformation($"云端signalr数据格式不匹配,{message?.ToJsonString()}");
-                    }
-                });
-                await hubConnection.StartAsync();
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError("初次启动连接SignalR失败,等待重连......");
-                int retryCount = 0;
-                const int maxRetries = 360;
-                const int retryDelaySeconds = 10;
-                while (retryCount < maxRetries)
-                {
-                    try
-                    {
-                        await Task.Delay(retryDelaySeconds * 1000); // 等待一段时间后重试  
-                        await hubConnection.StartAsync();
-                        _logger.LogInformation("SignalR连接成功(重试后)!");
-                        break; // 连接成功,退出循环  
-                    }
-                    catch (Exception retryEx)
-                    {
-                        retryCount++;
-                        _logger.LogInformation($"SignalR连接重试失败: {retryEx.Message}。重试次数: {retryCount}/{maxRetries}");
-                        // 可以在这里决定是否因为某种原因停止重试  
-                        if (retryCount == maxRetries)
-                        {
-                            _logger.LogInformation("达到最大重试次数,停止重试。");
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-    public class ExponentialBackoffReconnectPolicy : IRetryPolicy
-    {
-        private readonly TimeSpan _retryInterval;
-
-        private int _retryCount;
-        public int MaxRetryCount { get; set; } = int.MaxValue;
-        public readonly ILogger<SignalRCloudClientHub> _logger;
-        public ExponentialBackoffReconnectPolicy(TimeSpan retryInterval, ILogger<SignalRCloudClientHub> logger)
-        {
-            _retryInterval = retryInterval;
-            _retryCount = 0;
-            _logger = logger;
-        }
-
-        public TimeSpan? NextRetryDelay(RetryContext retryContext)
-        {
-            _logger.LogInformation($"重连次数: {_retryCount}");
-            if (_retryCount < MaxRetryCount)
-            {
-                _retryCount++;
-                // 计算下一次重连的延迟时间
-                return _retryInterval;
-            }
-            return null; // 达到最大重连次数后不再重连
-        }
-
-        public void Reset()
-        {
-            _retryCount = 0;
-        }
-    }
-}

+ 0 - 219
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRHost/SignalRExamServerHub.cs

@@ -1,219 +0,0 @@
-using IES.ExamServer.Helper;
-using IES.ExamServer.Models;
-using IES.ExamServer.Services;
-using Microsoft.AspNetCore.SignalR;
-using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Primitives;
-using System.Text.Json;
-
-
-namespace IES.ExamServer.DI.SignalRHost
-{
-    public static class SignalRExamServerHubExtension
-    {
-        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
-                {
-                    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;
-                    }
-                }
-                catch (Exception ex)
-                {
-                    code=500;
-                    msg = $"{ex.Message},{ex.StackTrace}";
-                }
-            }
-            else
-            {
-                code= 400;
-                msg="未连接客户端";
-            }
-            logger.LogData<object>(new { code, msg, data = messageBody }, content.dataId!);
-        }
-    }
-    public  class SignalRExamServerHub : Hub<IClient>
-    {
-        private readonly ILogger<SignalRExamServerHub> _logger;
-        private readonly IMemoryCache _memoryCache;
-      
-        public SignalRExamServerHub(ILogger<SignalRExamServerHub> logger,IMemoryCache memoryCache)
-        {
-            _logger = logger;
-            _memoryCache = memoryCache;
-        }
-
-        // <summary>
-        /// 这需要继承Hub来创建中心,并向中心添加方法,客户端可以调用标识符为public的方法
-        /// </summary>
-        /// <param name="user"></param>
-        /// <param name="message"></param>
-        /// <returns></returns>
-
-        public  void ReceiveMessage(string clientId, string grant_type, string contentmsg )
-        {
-            _logger.LogInformation($"收到客户端的消息{contentmsg}");
-        }
-   
-        /// <summary>
-        /// 客户连接成功时触发
-        /// </summary>
-        /// <returns></returns>
-        public override async Task OnConnectedAsync() 
-        {
-            ServerDevice device = _memoryCache.Get<ServerDevice>(Constant._KeyServerDevice);
-            var connid = Context.ConnectionId;
-            var httpContext = Context.GetHttpContext();
-            if (httpContext != null) 
-            {
-                //wss://www.winteach.cn/signalr/notify?grant_type=wechat_qrcode&scene=0a75aca57536490ba00fe62e27bb8f6c&id=U2MNiCFNPPuVcw2gUI_gRA
-                //wss://www.winteach.cn/signalr/notify?grant_type=bookjs_api&clientid={clientid}&id=客户端自动生成的
-                httpContext.Request.Query.TryGetValue("grant_type", out StringValues grant_type);
-                httpContext.Request.Query.TryGetValue("clientid", out StringValues clientid);
-                await Groups.AddToGroupAsync(connid, grant_type!);
-                if (!clientid.Equals(StringValues.Empty) && !grant_type.Equals(StringValues.Empty)) 
-                {
-                    var client = new SignalRClient
-                    {
-                        connid = connid,
-                        grant_type = grant_type,
-                        clientid= clientid,//浏览器生成的客户端设备id
-                        serverid=device.deviceId,//服务器设备id
-                    };
-                   
-                    switch (true) 
-                    {
-                        // 检查文件
-                        case bool when grant_type.Equals(Constant._Message_grant_type_check_file):
-                            {
-                                _memoryCache.Set($"{Constant._KeySignalRClientClients}:{clientid}", client);
-                                _memoryCache.Set($"{Constant._KeySignalRClientConnects}:{connid}", client);
-                                await SendConnection(connid, new ConnectionMessageBody
-                                {
-                                    connid=connid,
-                                    clientid = clientid,
-                                    grant_type = grant_type,
-                                    content = $"连接成功",
-                                    type=Constant._Message_type_message,
-                                    status = Constant._Message_status_success,
-                                    time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
-                                    
-                                });
-                                break;
-                            }
-                        // 扫码登录
-                        case bool when grant_type.Equals(Constant._Message_grant_type_ies_qrcode_login):
-                            {
-                                _memoryCache.Set($"{Constant._KeySignalRClientClients}:{clientid}", client);
-                                _memoryCache.Set($"{Constant._KeySignalRClientConnects}:{connid}", client);
-                                await SendConnection(connid, new ConnectionMessageBody
-                                {
-                                    connid=connid,
-                                    clientid = clientid,
-                                    grant_type = grant_type,
-                                    content = $"连接成功",
-                                    type=Constant._Message_type_message,
-                                    status = Constant._Message_status_success,
-                                    time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
-
-                                });
-                                break;
-                            }
-                        // 下载文件
-                        case bool when grant_type.Equals(Constant._Message_grant_type_download_file):
-                            {
-                                _memoryCache.Set($"{Constant._KeySignalRClientClients}:{clientid}", client);
-                                _memoryCache.Set($"{Constant._KeySignalRClientConnects}:{connid}", client);
-                                await SendConnection(connid, new ConnectionMessageBody
-                                {
-                                    connid=connid,
-                                    clientid = clientid,
-                                    grant_type = grant_type,
-                                    content = $"连接成功",
-                                    type=Constant._Message_type_message,
-                                    status = Constant._Message_status_success,
-                                    time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
-
-                                });
-                                break;
-                            }
-                        // 上传数据
-                        case bool when grant_type.Equals(Constant._Message_grant_type_upload_data):
-                            {
-                                _memoryCache.Set($"{Constant._KeySignalRClientClients}:{clientid}", client);
-                                _memoryCache.Set($"{Constant._KeySignalRClientConnects}:{connid}", client);
-                                await SendConnection(connid, new ConnectionMessageBody
-                                {
-                                    connid=connid,
-                                    clientid = clientid,
-                                    grant_type = grant_type,
-                                    content = $"连接成功",
-                                    type=Constant._Message_type_message,
-                                    status = Constant._Message_status_success,
-                                    time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
-
-                                });
-                                break;
-                            }
-                    }
-                }
-            }
-        }
-        public async Task SendConnection(string connectionId, ConnectionMessageBody msg)
-        {
-            await Clients.Client(connectionId).ReceiveConnection(msg);
-        }
-        public async override Task OnDisconnectedAsync(Exception? exception)
-        {
-            var connid = Context.ConnectionId;
-            SignalRClient  signalRClient =   _memoryCache.Get<SignalRClient>($"{Constant._KeySignalRClientConnects}:{connid}");
-            if (signalRClient!=null)
-            {
-                _memoryCache.Remove($"{Constant._KeySignalRClientConnects}:{connid}");
-                _memoryCache.Remove($"{Constant._KeySignalRClientClients}:{signalRClient.clientid}");
-                await Groups.RemoveFromGroupAsync(connid, signalRClient.grant_type!);
-            }
-        }
-    }
-    public interface IClient
-    {
-        Task ReceiveMessage(MessageBody message);
-        Task ReceiveConnection(MessageBody message);
-        Task ReceiveDisConnection(MessageBody message);
-    }
-  
-    
-}

+ 0 - 29
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Filters/AspNetCoreBuilderServiceCollectionExtensions.cs

@@ -1,29 +0,0 @@
-using Microsoft.AspNetCore.Mvc.Filters;
-using Microsoft.AspNetCore.Mvc;
-
-namespace IES.ExamServer.Filters
-{
-    public static    class AspNetCoreBuilderServiceCollectionExtensions
-    {
-        /// <summary>
-        /// 注册 Mvc 过滤器
-        /// </summary>
-        /// <typeparam name="TFilter"></typeparam>
-        /// <param name="services"></param>
-        /// <param name="configure"></param>
-        /// <returns></returns>
-        public static IServiceCollection AddMvcFilter<TFilter>(this IServiceCollection services, Action<MvcOptions> configure = default)
-            where TFilter : IFilterMetadata
-        {
-            services.Configure<MvcOptions>(options =>
-            {
-                options.Filters.Add<TFilter>();
-
-                // 其他额外配置
-                configure?.Invoke(options);
-            });
-
-            return services;
-        }
-    }
-}

+ 0 - 114
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Filters/AuthTokenAttribute.cs

@@ -1,114 +0,0 @@
-using IES.ExamLib.Models;
-using Microsoft.AspNetCore.Mvc;
-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
-{
-    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
-    public class AuthTokenAttribute : Attribute 
-    {
-        public string[]? Roles { get; set; }
-        public AuthTokenAttribute(params string[] roles)
-        {
-            Roles = roles;
-        }
-    }
-    public class AuthTokenActionFilter : IActionFilter
-    {
-        public     void OnActionExecuting(ActionExecutingContext context)
-        {
-            var authtoken = context.HttpContext.GetXAuth("AuthToken");
-            var TimeZone = 8;
-            var authTokenAttribute = context.ActionDescriptor.EndpointMetadata.OfType<AuthTokenAttribute>().FirstOrDefault();
-            bool needParse = false;
-            if (authTokenAttribute!=null)
-            {
-
-                if (string.IsNullOrWhiteSpace(authtoken) ||  !JwtAuthExtension.ValidateAuthToken(authtoken, ExamConstant.JwtSecretKey))
-                {
-                    context.Result = new Microsoft.AspNetCore.Mvc.UnauthorizedResult();
-                }
-                else {
-                    needParse=true;
-                }
-            }
-            else { needParse=true; }
-            if (needParse)
-            {
-                if (!string.IsNullOrWhiteSpace(authtoken) && JwtAuthExtension.ValidateAuthToken(authtoken, ExamConstant.JwtSecretKey))
-                {
-                    //string msg = "";
-                    //int code = 0;
-
-                    string? id = string.Empty, name = string.Empty, picture = string.Empty, school = string.Empty, scope = string.Empty, timzone = string.Empty;
-                    if (context.HttpContext.Request.Headers.TryGetValue("Time-Zone", out StringValues value)  && int.TryParse($"{value}", out int tz))
-                    {
-                        TimeZone=tz;
-                    }
-                    var jwt = new JwtSecurityTokenHandler().ReadJwtToken(authtoken);
-                    id = jwt.Payload.Sub;
-                    //school = jwt.Payload.Azp;
-                    name = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("name"))?.Value;
-                    picture = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("picture"))?.Value;
-                    scope = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("scope"))?.Value;
-                    timzone = jwt.Claims.FirstOrDefault(claim => claim.Type.Equals("timzone"))?.Value;
-                    bool pass = false;
-                    List<string>? _roles = jwt.Claims.Where(c => c.Type.Equals("roles")).Select(x => x.Value)?.ToList();
-
-
-                    if (authTokenAttribute!=null)
-                    {
-                        if (_roles!=null && authTokenAttribute.Roles!=null&& authTokenAttribute.Roles.Intersect(_roles).Any())
-                        {
-                            pass = true;
-                        }
-                    }
-                    else
-                    {
-                        pass=true;
-                    }
-                    if (pass)
-                    {
-                        //未标记
-                        context.HttpContext.Items.Add("ID", id);
-                        context.HttpContext.Items.Add("Name", name);
-                        context.HttpContext.Items.Add("Picture", picture);
-                        //context.HttpContext.Items.Add("School", school);
-                        context.HttpContext.Items.Add("Roles", _roles);
-                        context.HttpContext.Items.Add("Scope", scope);
-                        context.HttpContext.Items.Add("TimeZone", TimeZone);
-                    }
-                    else
-                    {
-                        context.Result = new Microsoft.AspNetCore.Mvc.UnauthorizedResult();
-                    }
-                }
-                else 
-                {
-                    var _roles = new List<string> { "visitor" }; // 默认角色,访客角色
-                    context.HttpContext.Items.Add("Roles", _roles);
-                    context.HttpContext.Items.Add("TimeZone", TimeZone);
-                    context.HttpContext.Items.Add("ID", $"{DateTimeOffset.Now.ToUnixTimeSeconds()}");
-                    context.HttpContext.Items.Add("Name", $"访客{Random.Shared.Next(100,999)}");
-                    context.HttpContext.Items.Add("Picture", null);
-                    context.HttpContext.Items.Add("Scope", "visitor");
-                }
-            }
-            else 
-            { 
-                context.Result = new Microsoft.AspNetCore.Mvc.UnauthorizedResult();
-            }
-             
-        }
-
-        public void OnActionExecuted(ActionExecutedContext context)
-        {
-            // 在 Action 执行后不需要做任何处理
-        }
-    }
-}

+ 0 - 38
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Helpers/CollectionHelper.cs

@@ -1,38 +0,0 @@
-namespace IES.ExamServer.Helpers
-{
-    public static class CollectionHelper
-    {
-        /// <summary>
-        /// 判断集合是否为空
-        /// </summary>
-        /// <param name="collection"></param>
-        /// <returns></returns>
-        public static bool IsEmpty<T>(this IEnumerable<T>? collection)
-        {
-            if (collection != null && collection.Any())
-            {
-                return false;
-            }
-            else
-            {
-                return true;
-            }
-        }
-        /// <summary>
-        /// 判断集合是否不为空
-        /// </summary>
-        /// <param name="collection"></param>
-        /// <returns></returns>
-        public static bool IsNotEmpty<T>(this IEnumerable<T>? collection)
-        {
-            if (collection != null && collection.Any())
-            {
-                return true;
-            }
-            else
-            {
-                return false;
-            }
-        }
-    }
-}

+ 0 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Helpers/Constant.cs


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