From 6c34d8df858b0c795dd06da0962c22f9d1c40bd2 Mon Sep 17 00:00:00 2001
From: ZhangXianQiang <1135831638@qq.com>
Date: 星期四, 13 六月 2024 14:50:48 +0800
Subject: [PATCH] feat(在线培训):添加在线培训流程

---
 dist-electron/background.js         |   33 ++
 src/preload.js                      |    5 
 package-lock.json                   |  336 ++++++++++++++++++++++++++++++
 src/views/train/data-list/index.vue |   78 +++++++
 index.html                          |    6 
 src/views/train/index.vue           |   61 +++++
 package.json                        |    1 
 src/background.js                   |   38 +++
 src/views/meet/index.vue            |   30 ++
 src/assets/image/list-card-bg.jpg   |    0 
 src/router/index.js                 |   17 +
 src/views/menu/index.vue            |   11 
 12 files changed, 594 insertions(+), 22 deletions(-)

diff --git a/dist-electron/background.js b/dist-electron/background.js
index aedb9c8..a903300 100644
--- a/dist-electron/background.js
+++ b/dist-electron/background.js
@@ -1,15 +1,18 @@
 "use strict";
-const { app, BrowserWindow, screen } = require("electron");
+const { app, BrowserWindow, screen, globalShortcut, ipcMain } = require("electron");
 const { join } = require("path");
 process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true";
-const createWindow = () => {
-  const { width, height } = screen.getPrimaryDisplay().bounds;
+const createWindow = (width, height) => {
   const win = new BrowserWindow({
     width,
     height,
     minWidth: 1280,
-    minHeight: 720
+    minHeight: 720,
+    webPreferences: {
+      preload: join(__dirname, "preload.js")
+    }
   });
+  win.maximize();
   if (process.env.VITE_DEV_SERVER_URL) {
     win.loadURL(process.env.VITE_DEV_SERVER_URL);
     win.webContents.openDevTools();
@@ -18,11 +21,31 @@
   }
 };
 app.whenReady().then(() => {
-  createWindow();
+  const { width, height } = screen.getPrimaryDisplay().bounds;
+  createWindow(width, height);
   app.on("activate", () => {
     if (BrowserWindow.getAllWindows().length === 0)
       createWindow();
   });
+  ipcMain.on("open-new-window", () => {
+    const childWin = new BrowserWindow({
+      width,
+      height,
+      minWidth: width,
+      minHeight: height,
+      webPreferences: {
+        preload: join(__dirname, "preload.js")
+      }
+    });
+    childWin.maximize();
+    if (process.env.VITE_DEV_SERVER_URL) {
+      childWin.loadURL(process.env.VITE_DEV_SERVER_URL + "#/meet");
+    } else {
+      childWin.loadFile(join(__dirname, "../dist/index.html"), {
+        hash: "/meet"
+      });
+    }
+  });
 });
 app.on("window-all-closed", () => {
   if (process.platform !== "darwin")
diff --git a/index.html b/index.html
index 8388c4b..1f7f6f8 100644
--- a/index.html
+++ b/index.html
@@ -5,9 +5,15 @@
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Vite + Vue</title>
+    <script src='https://meet.jit.si/external_api.js'></script>
   </head>
   <body>
     <div id="app"></div>
     <script type="module" src="/src/main.js"></script>
+    <script>
+      window.onload = () => {
+        window.JitsiMeetExternalAPI = JitsiMeetExternalAPI;
+      }
+    </script>
   </body>
 </html>
diff --git a/package-lock.json b/package-lock.json
index 693490d..a7c2315 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
       "name": "jxkg-new-ui",
       "version": "0.0.0",
       "dependencies": {
+        "@jitsi/electron-sdk": "^6.0.40",
         "axios": "^1.7.2",
         "dayjs": "^1.11.11",
         "element-plus": "^2.7.3",
@@ -832,6 +833,48 @@
         "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
       }
     },
+    "node_modules/@jitsi/electron-sdk": {
+      "version": "6.0.40",
+      "resolved": "https://registry.npmmirror.com/@jitsi/electron-sdk/-/electron-sdk-6.0.40.tgz",
+      "integrity": "sha512-dUr74XakxX4Keiq8Z2VSNhKw5W3e2GQy4TVh5JhhLi/lltxito5ulZzak22rlHaL1qQ3u35hI2+Lale7x2FFIQ==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "@jitsi/logger": "^2.0.2",
+        "@jitsi/robotjs": "^0.6.13",
+        "electron-store": "^8.0.1",
+        "node-addon-api": "^8.0.0",
+        "node-gyp-build": "4.8.1",
+        "postis": "^2.2.0"
+      }
+    },
+    "node_modules/@jitsi/electron-sdk/node_modules/node-addon-api": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-8.0.0.tgz",
+      "integrity": "sha512-ipO7rsHEBqa9STO5C5T10fj732ml+5kLN1cAG8/jdHd56ldQeGj3Q7+scUS+VHK/qy1zLEwC4wMK5+yM0btPvw==",
+      "engines": {
+        "node": "^18 || ^20 || >= 21"
+      }
+    },
+    "node_modules/@jitsi/logger": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/@jitsi/logger/-/logger-2.0.2.tgz",
+      "integrity": "sha512-qwbpRwuwkBFgh0F5jivq/5fAm46yVoXURc5LCklEs8lAShYVangFEXKW7RLpZuZ5nQnrHrlvU8MswQNREmvahg=="
+    },
+    "node_modules/@jitsi/robotjs": {
+      "version": "0.6.13",
+      "resolved": "https://registry.npmmirror.com/@jitsi/robotjs/-/robotjs-0.6.13.tgz",
+      "integrity": "sha512-uFxRQp83jbKfMzk3lYIjgevy1X1dU/Z7OMPnqfdO1LcyPaXsOf+FCWSxM/KIxz0PvbBbORxUzuae5O5dAF5Kqw==",
+      "hasInstallScript": true,
+      "dependencies": {
+        "node-addon-api": "^4.2.0",
+        "node-gyp-build": "^4.3.0"
+      }
+    },
+    "node_modules/@jitsi/robotjs/node_modules/node-addon-api": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-4.3.0.tgz",
+      "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="
+    },
     "node_modules/@jridgewell/gen-mapping": {
       "version": "0.3.5",
       "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
@@ -1623,6 +1666,42 @@
         "url": "https://github.com/sponsors/epoberezkin"
       }
     },
+    "node_modules/ajv-formats": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz",
+      "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+      "dependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ajv": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/ajv-formats/node_modules/ajv": {
+      "version": "8.16.0",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.16.0.tgz",
+      "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2",
+        "uri-js": "^4.4.1"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ajv-formats/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+    },
     "node_modules/ajv-keywords": {
       "version": "3.5.2",
       "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
@@ -1908,6 +1987,14 @@
       "dev": true,
       "engines": {
         "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/atomically": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmmirror.com/atomically/-/atomically-1.7.0.tgz",
+      "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==",
+      "engines": {
+        "node": ">=10.12.0"
       }
     },
     "node_modules/autoprefixer": {
@@ -2440,6 +2527,60 @@
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
       "dev": true
     },
+    "node_modules/conf": {
+      "version": "10.2.0",
+      "resolved": "https://registry.npmmirror.com/conf/-/conf-10.2.0.tgz",
+      "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==",
+      "dependencies": {
+        "ajv": "^8.6.3",
+        "ajv-formats": "^2.1.1",
+        "atomically": "^1.7.0",
+        "debounce-fn": "^4.0.0",
+        "dot-prop": "^6.0.1",
+        "env-paths": "^2.2.1",
+        "json-schema-typed": "^7.0.3",
+        "onetime": "^5.1.2",
+        "pkg-up": "^3.1.0",
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/conf/node_modules/ajv": {
+      "version": "8.16.0",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.16.0.tgz",
+      "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2",
+        "uri-js": "^4.4.1"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/conf/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+    },
+    "node_modules/conf/node_modules/semver": {
+      "version": "7.6.2",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.6.2.tgz",
+      "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/confbox": {
       "version": "0.1.7",
       "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.7.tgz",
@@ -2580,6 +2721,17 @@
       "version": "1.11.11",
       "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.11.tgz",
       "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
+    },
+    "node_modules/debounce-fn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/debounce-fn/-/debounce-fn-4.0.0.tgz",
+      "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==",
+      "dependencies": {
+        "mimic-fn": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
     },
     "node_modules/debug": {
       "version": "4.3.4",
@@ -2805,6 +2957,20 @@
       },
       "engines": {
         "node": ">=8"
+      }
+    },
+    "node_modules/dot-prop": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-6.0.1.tgz",
+      "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
+      "dependencies": {
+        "is-obj": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
       }
     },
     "node_modules/dotenv": {
@@ -3047,6 +3213,29 @@
         "node": ">= 10.0.0"
       }
     },
+    "node_modules/electron-store": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmmirror.com/electron-store/-/electron-store-8.2.0.tgz",
+      "integrity": "sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==",
+      "dependencies": {
+        "conf": "^10.2.0",
+        "type-fest": "^2.17.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/electron-store/node_modules/type-fest": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-2.19.0.tgz",
+      "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+      "engines": {
+        "node": ">=12.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/electron-to-chromium": {
       "version": "1.4.788",
       "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.788.tgz",
@@ -3108,7 +3297,6 @@
       "version": "2.2.1",
       "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz",
       "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
-      "dev": true,
       "engines": {
         "node": ">=6"
       }
@@ -3252,8 +3440,7 @@
     "node_modules/fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
-      "dev": true
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
     },
     "node_modules/fast-glob": {
       "version": "3.3.2",
@@ -3314,6 +3501,17 @@
       },
       "engines": {
         "node": ">=8"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz",
+      "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+      "dependencies": {
+        "locate-path": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
       }
     },
     "node_modules/follow-redirects": {
@@ -3913,6 +4111,14 @@
         "node": ">=0.12.0"
       }
     },
+    "node_modules/is-obj": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz",
+      "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/isarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
@@ -4030,6 +4236,11 @@
       "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
       "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
       "dev": true
+    },
+    "node_modules/json-schema-typed": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmmirror.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz",
+      "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A=="
     },
     "node_modules/json-stringify-safe": {
       "version": "5.0.1",
@@ -4202,6 +4413,18 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
+    "node_modules/locate-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz",
+      "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+      "dependencies": {
+        "p-locate": "^3.0.0",
+        "path-exists": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
@@ -4357,6 +4580,14 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/mimic-fn": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-3.1.0.tgz",
+      "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/mimic-response": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz",
@@ -4486,6 +4717,16 @@
       "dev": true,
       "optional": true
     },
+    "node_modules/node-gyp-build": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmmirror.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz",
+      "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==",
+      "bin": {
+        "node-gyp-build": "bin.js",
+        "node-gyp-build-optional": "optional.js",
+        "node-gyp-build-test": "build-test.js"
+      }
+    },
     "node_modules/node-releases": {
       "version": "2.0.14",
       "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.14.tgz",
@@ -4564,6 +4805,28 @@
         "wrappy": "1"
       }
     },
+    "node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/onetime/node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/p-cancelable": {
       "version": "2.1.1",
       "resolved": "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-2.1.1.tgz",
@@ -4573,11 +4836,52 @@
         "node": ">=8"
       }
     },
+    "node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz",
+      "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+      "dependencies": {
+        "p-limit": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/pako": {
       "version": "1.0.11",
       "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
       "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
       "dev": true
+    },
+    "node_modules/path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+      "engines": {
+        "node": ">=4"
+      }
     },
     "node_modules/path-is-absolute": {
       "version": "1.0.1",
@@ -4734,6 +5038,17 @@
         "confbox": "^0.1.7",
         "mlly": "^1.7.0",
         "pathe": "^1.1.2"
+      }
+    },
+    "node_modules/pkg-up": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/pkg-up/-/pkg-up-3.1.0.tgz",
+      "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
+      "dependencies": {
+        "find-up": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
     "node_modules/plist": {
@@ -4898,6 +5213,11 @@
       "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
       "dev": true
     },
+    "node_modules/postis": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/postis/-/postis-2.2.0.tgz",
+      "integrity": "sha512-MKoPQqjjNU6aDr9IN3VFMZk+ND2jXjBGVHdU/Kgyffb8mr11ZQSE7QwmpLw+iYRjF8YIDSgdVoaPLugJ0ycHYg=="
+    },
     "node_modules/process-nextick-args": {
       "version": "2.0.1",
       "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -4945,7 +5265,6 @@
       "version": "2.3.1",
       "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
       "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
-      "dev": true,
       "engines": {
         "node": ">=6"
       }
@@ -5050,6 +5369,14 @@
       "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
       "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
       "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
       "engines": {
         "node": ">=0.10.0"
       }
@@ -6077,7 +6404,6 @@
       "version": "4.4.1",
       "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
       "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
-      "dev": true,
       "dependencies": {
         "punycode": "^2.1.0"
       }
diff --git a/package.json b/package.json
index 4b74a76..9bf3fc5 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
     "electron:dev": "vite && electron"
   },
   "dependencies": {
+    "@jitsi/electron-sdk": "^6.0.40",
     "axios": "^1.7.2",
     "dayjs": "^1.11.11",
     "element-plus": "^2.7.3",
diff --git a/src/assets/image/list-card-bg.jpg b/src/assets/image/list-card-bg.jpg
new file mode 100644
index 0000000..34346f6
--- /dev/null
+++ b/src/assets/image/list-card-bg.jpg
Binary files differ
diff --git a/src/background.js b/src/background.js
index c7c4802..1cc5e7a 100644
--- a/src/background.js
+++ b/src/background.js
@@ -1,5 +1,5 @@
 // src-electron/main.js
-const { app, BrowserWindow,screen  } = require('electron');
+const { app, BrowserWindow, screen, globalShortcut, ipcMain } = require('electron');
 const { join } = require('path');
 
 // 灞忚斀瀹夊叏璀﹀憡
@@ -7,16 +7,18 @@
 process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
 
 // 鍒涘缓娴忚鍣ㄧ獥鍙f椂锛岃皟鐢ㄨ繖涓嚱鏁般��
-const createWindow = () => {
-    const {width, height} = screen.getPrimaryDisplay().bounds;
+const createWindow = (width, height) => {
     const win = new BrowserWindow({
         width: width,
         height: height,
         minWidth: 1280,
         minHeight: 720,
+        webPreferences: {
+            preload: join(__dirname, 'preload.js')
+        }
     });
 
-    // win.loadURL('http://localhost:3000')
+    win.maximize();
     // development妯″紡
     if (process.env.VITE_DEV_SERVER_URL) {
         win.loadURL(process.env.VITE_DEV_SERVER_URL);
@@ -29,12 +31,38 @@
 
 // Electron 浼氬湪鍒濆鍖栧悗骞跺噯澶�
 app.whenReady().then(() => {
-    createWindow();
+    const { width, height } = screen.getPrimaryDisplay().bounds;
+    createWindow(width, height);
     app.on('activate', () => {
         if (BrowserWindow.getAllWindows().length === 0) createWindow();
+    });
+
+    // 鐩戝惉鎵撳紑鏂扮獥鍙�
+    ipcMain.on('open-new-window', () => {
+        const childWin = new BrowserWindow({
+            width: width,
+            height: height,
+            minWidth: width,
+            minHeight: height,
+            webPreferences: {
+                preload: join(__dirname, 'preload.js')
+            }
+        });
+        childWin.maximize();
+        // development妯″紡
+        if (process.env.VITE_DEV_SERVER_URL) {
+            childWin.loadURL(process.env.VITE_DEV_SERVER_URL + '#/meet');
+        } else {
+            childWin.loadFile(join(__dirname, '../dist/index.html'), {
+                hash: '/meet'
+            });
+        }
     });
 });
 
 app.on('window-all-closed', () => {
     if (process.platform !== 'darwin') app.quit();
 });
+
+
+
diff --git a/src/preload.js b/src/preload.js
new file mode 100644
index 0000000..6540bd3
--- /dev/null
+++ b/src/preload.js
@@ -0,0 +1,5 @@
+const { contextBridge, ipcRenderer } = require('electron');
+
+contextBridge.exposeInMainWorld('electron', {
+  openNewWindow: (arg) => ipcRenderer.send('open-new-window', arg)
+});
\ No newline at end of file
diff --git a/src/router/index.js b/src/router/index.js
index 91a47a3..802dfd4 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,11 +1,11 @@
-import { createMemoryHistory, createRouter } from 'vue-router';
+import { createWebHashHistory, createRouter } from 'vue-router';
 
 import Layout from '@/layout/index.vue';
 
 const routes = [
   {
     path: '/',
-    redirect: '/exam'
+    redirect: '/index'
   },
 
   {
@@ -24,7 +24,16 @@
       },
     ]
   },
-  
+  // 鍦ㄧ嚎鍩硅
+  {
+    path: '/train',
+    component: () => import('@/views/train/index.vue'),
+  },
+  // 浼氳
+  {
+    path: '/meet',
+    component: () => import('@/views/meet/index.vue'),
+  },
   // 鑰冭瘯鍒楄〃
   {
     path: '/exam-list',
@@ -44,7 +53,7 @@
 ];
 
 const router = createRouter({
-  history: createMemoryHistory(),
+  history: createWebHashHistory(),
   routes,
 });
 
diff --git a/src/views/meet/index.vue b/src/views/meet/index.vue
new file mode 100644
index 0000000..cd6e192
--- /dev/null
+++ b/src/views/meet/index.vue
@@ -0,0 +1,30 @@
+<template>
+  <div class="meet-container w-screen h-screen">
+    <div id="meet" ref="meet"></div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue';
+const meet = ref(null);
+onMounted(() => {
+  const width = window.innerWidth;
+  const height = window.innerHeight;
+  const domain = 'ycl.easyblog.vip:8443';
+  const options = {
+    roomName: 'test',
+    width: width,
+    height: height,
+    parentNode: meet.value,
+    lang: 'zh_CN',
+    configOverwrite: {
+      prejoinConfig: {
+        enabled: false
+      }
+    },
+  };
+  const api = new JitsiMeetExternalAPI(domain, options);
+});
+</script>
+
+<style lang="scss" scoped></style>
\ No newline at end of file
diff --git a/src/views/menu/index.vue b/src/views/menu/index.vue
index 4ff1669..668c68a 100644
--- a/src/views/menu/index.vue
+++ b/src/views/menu/index.vue
@@ -25,7 +25,7 @@
 
 <script setup>
 import { ref } from 'vue';
-import {useRouter} from 'vue-router';
+import { useRouter } from 'vue-router';
 
 const router = useRouter();
 
@@ -33,6 +33,11 @@
   {
     name: '璇剧▼',
     categroy: [
+      {
+        title: '鍦ㄧ嚎鍩硅',
+        iconPath: new URL('@/assets/icons/icon2.png', import.meta.url).href,
+        path: '/train'
+      },
       {
         title: '鎴戠殑璇剧▼',
         iconPath: new URL('@/assets/icons/icon1.png', import.meta.url).href,
@@ -58,10 +63,10 @@
 ]);
 
 const menuClick = (item) => {
-  if(item.path) {
+  if (item.path) {
     router.push(item.path);
   }
-}
+};
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/views/train/data-list/index.vue b/src/views/train/data-list/index.vue
new file mode 100644
index 0000000..72bfe2c
--- /dev/null
+++ b/src/views/train/data-list/index.vue
@@ -0,0 +1,78 @@
+<template>
+  <div class="list-container w-full h-full">
+    <el-scrollbar>
+      <el-row :gutter="20">
+        <el-col :span="6" v-for="item in dataList">
+          <el-card shadow="hover" class="list-card cursor-pointer" :body-style="{ padding: 0 }" @click="itemClick(item)">
+            <div class="img-container w-full">
+              <img src="@/assets/image/list-card-bg.jpg" class="w-full">
+            </div>
+            <div class="item-info p-3">
+              <div class="info-title font-bold">{{ item.title }}</div>
+              <div class="info-teacher flex text-sm text-gray-500">
+                <div class="info-label">涓昏:</div>
+                <div class="info-text">{{ item.teacher }}</div>
+              </div>
+              <div class="info-time flex text-sm text-gray-500">
+                <div class="info-label">寮�濮嬫椂闂�:</div>
+                <div class="info-text">{{ item.startTime }}</div>
+              </div>
+              <div class="info-time flex text-sm text-gray-500">
+                <div class="info-label">缁撴潫鏃堕棿:</div>
+                <div class="info-text">{{ item.endTime }}</div>
+              </div>
+            </div>
+          </el-card>
+        </el-col>
+      </el-row>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { Timer } from '@element-plus/icons-vue';
+import { useRouter } from 'vue-router';
+const router = useRouter();
+
+const dataList = ref([
+  {
+    title: '娴嬭瘯1',
+    startTime: '2024-6-13 8:00',
+    endTime: '2024-6-13 8:00',
+    teacher: '娴嬭瘯娴嬭瘯',
+    roomName: 'test'
+  }
+])
+
+
+const itemClick = (item) => {
+  if(window.electron) {
+    window.electron.openNewWindow();
+  }
+}
+
+</script>
+
+<style lang="scss" scoped>
+.item {
+  width: 100%;
+  min-height: 120px;
+}
+
+.bottom-item {
+  margin-right: 30px;
+}
+
+.img-container {
+  object-fit: cover;
+  object-position: center;
+  width: 100%;
+  max-height: 160px;
+  overflow: hidden;
+}
+
+.list-card {
+  border-radius: 10px;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/train/index.vue b/src/views/train/index.vue
new file mode 100644
index 0000000..ca45a28
--- /dev/null
+++ b/src/views/train/index.vue
@@ -0,0 +1,61 @@
+<template>
+  <div class="train-container w-screen h-screen bg-slate-50 flex flex-col items-center">
+    <NormalHeader class="shrink-0"></NormalHeader>
+
+    <div class="list-container container grow relative">
+      <div class="list-content absolute top-0 bottom-0 left-0 right-0 py-4">
+        <div class="list-wrapper w-full h-full">
+          <el-card class="h-full" :body-style="{ height: '100%' }">
+            <div class="card-wrapper w-full h-full flex flex-col px-8 box-border">
+              <div class="card-header flex justify-between items-center shrink-0">
+                <div class="header-tab">
+                  <el-tabs v-model="activeName" @tab-click="handleClick">
+                    <el-tab-pane label="鍏ㄩ儴" name="1"></el-tab-pane>
+                    <el-tab-pane label="鏈紑濮�" name="2"></el-tab-pane>
+                    <el-tab-pane label="杩涜涓�" name="3"></el-tab-pane>
+                    <el-tab-pane label="宸茬粨鏉�" name="4"></el-tab-pane>
+                  </el-tabs>
+                </div>
+                <div class="header-search flex items-center">
+                  <el-input v-model="searchText" placeholder="璇疯緭鍏ヨ�冭瘯鍚嶇О" :prefix-icon="Search" />
+                  <el-button type="primary" class="ml-4">鎼滅储</el-button>
+                </div>
+              </div>
+
+              <div class="card-main flex-1 my-5 relative">
+                <div class="main-content absolute top-0 bottom-0 left-0 right-0">
+                  <DataList></DataList>
+
+                  <div id="meet" ref="meet"></div>
+                </div>
+              </div>
+
+              <div class="card-footer flex justify-center mb-7 shrink-0">
+                <el-pagination background layout="prev, pager, next" :total="1000" />
+              </div>
+            </div>
+          </el-card>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import NormalHeader from '@/components/NormalHeader/index.vue';
+import DataList from './data-list/index.vue';
+import { Search } from '@element-plus/icons-vue';
+const activeName = ref('1');
+const searchText = ref('');
+
+const handleClick = (tab, event) => {
+};
+
+</script>
+
+<style lang="scss" scoped>
+:deep(.el-tabs__nav-wrap:after) {
+  display: none;
+}
+</style>
\ No newline at end of file

--
Gitblit v1.8.0