mirror of
https://hub.gitmirror.com/https://github.com/ExamAware/ExamShowboard-Legacy.git
synced 2025-04-28 23:36:33 +00:00
feat: 上传所有文件
This commit is contained in:
commit
01964be6e2
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
dist
|
||||
out
|
||||
.gitignore
|
69
.eslintrc-auto-import.json
Normal file
69
.eslintrc-auto-import.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"globals": {
|
||||
"Component": true,
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"EffectScope": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
"InjectionKey": true,
|
||||
"PropType": true,
|
||||
"Ref": true,
|
||||
"VNode": true,
|
||||
"WritableComputedRef": true,
|
||||
"computed": true,
|
||||
"createApp": true,
|
||||
"customRef": true,
|
||||
"defineAsyncComponent": true,
|
||||
"defineComponent": true,
|
||||
"effectScope": true,
|
||||
"getCurrentInstance": true,
|
||||
"getCurrentScope": true,
|
||||
"h": true,
|
||||
"inject": true,
|
||||
"isProxy": true,
|
||||
"isReactive": true,
|
||||
"isReadonly": true,
|
||||
"isRef": true,
|
||||
"markRaw": true,
|
||||
"nextTick": true,
|
||||
"onActivated": true,
|
||||
"onBeforeMount": true,
|
||||
"onBeforeUnmount": true,
|
||||
"onBeforeUpdate": true,
|
||||
"onDeactivated": true,
|
||||
"onErrorCaptured": true,
|
||||
"onMounted": true,
|
||||
"onRenderTracked": true,
|
||||
"onRenderTriggered": true,
|
||||
"onScopeDispose": true,
|
||||
"onServerPrefetch": true,
|
||||
"onUnmounted": true,
|
||||
"onUpdated": true,
|
||||
"provide": true,
|
||||
"reactive": true,
|
||||
"readonly": true,
|
||||
"ref": true,
|
||||
"resolveComponent": true,
|
||||
"shallowReactive": true,
|
||||
"shallowReadonly": true,
|
||||
"shallowRef": true,
|
||||
"toRaw": true,
|
||||
"toRef": true,
|
||||
"toRefs": true,
|
||||
"toValue": true,
|
||||
"triggerRef": true,
|
||||
"unref": true,
|
||||
"useAttrs": true,
|
||||
"useCssModule": true,
|
||||
"useCssVars": true,
|
||||
"useRoute": true,
|
||||
"useRouter": true,
|
||||
"useSlots": true,
|
||||
"watch": true,
|
||||
"watchEffect": true,
|
||||
"watchPostEffect": true,
|
||||
"watchSyncEffect": true
|
||||
}
|
||||
}
|
17
.eslintrc.cjs
Normal file
17
.eslintrc.cjs
Normal file
@ -0,0 +1,17 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution');
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-recommended',
|
||||
'@electron-toolkit',
|
||||
'@electron-toolkit/eslint-config-ts/eslint-recommended',
|
||||
'@vue/eslint-config-typescript/recommended',
|
||||
'@vue/eslint-config-prettier'
|
||||
],
|
||||
rules: {
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/multi-word-component-names': 'off'
|
||||
}
|
||||
};
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
dist
|
||||
out
|
||||
.DS_Store
|
||||
*.log*
|
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
yarn commitlint
|
2
.npmrc
Normal file
2
.npmrc
Normal file
@ -0,0 +1,2 @@
|
||||
electron_mirror=https://npmmirror.com/mirrors/electron/
|
||||
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
|
6
.prettierignore
Normal file
6
.prettierignore
Normal file
@ -0,0 +1,6 @@
|
||||
out
|
||||
dist
|
||||
pnpm-lock.yaml
|
||||
LICENSE.md
|
||||
tsconfig.json
|
||||
tsconfig.*.json
|
4
.prettierrc.yaml
Normal file
4
.prettierrc.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
singleQuote: true
|
||||
semi: true
|
||||
printWidth: 100
|
||||
trailingComma: none
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["dbaeumer.vscode-eslint"]
|
||||
}
|
39
.vscode/launch.json
vendored
Normal file
39
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Main Process",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
|
||||
},
|
||||
"runtimeArgs": ["--sourcemap"],
|
||||
"env": {
|
||||
"REMOTE_DEBUGGING_PORT": "9222"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Debug Renderer Process",
|
||||
"port": 9222,
|
||||
"request": "attach",
|
||||
"type": "chrome",
|
||||
"webRoot": "${workspaceFolder}/src/renderer",
|
||||
"timeout": 60000,
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Debug All",
|
||||
"configurations": ["Debug Main Process", "Debug Renderer Process"],
|
||||
"presentation": {
|
||||
"order": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
29
.vscode/settings.json
vendored
Normal file
29
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"MicroPython.executeButton": [
|
||||
{
|
||||
"text": "▶",
|
||||
"tooltip": "运行",
|
||||
"alignment": "left",
|
||||
"command": "extension.executeFile",
|
||||
"priority": 3.5
|
||||
}
|
||||
],
|
||||
"MicroPython.syncButton": [
|
||||
{
|
||||
"text": "$(sync)",
|
||||
"tooltip": "同步",
|
||||
"alignment": "left",
|
||||
"command": "extension.execute",
|
||||
"priority": 4
|
||||
}
|
||||
]
|
||||
}
|
34
README.md
Normal file
34
README.md
Normal file
@ -0,0 +1,34 @@
|
||||
# dsz-exam-composer
|
||||
|
||||
An Electron application with Vue and TypeScript
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin)
|
||||
|
||||
## Project Setup
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
$ yarn
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
$ yarn dev
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
# For windows
|
||||
$ yarn build:win
|
||||
|
||||
# For macOS
|
||||
$ yarn build:mac
|
||||
|
||||
# For Linux
|
||||
$ yarn build:linux
|
||||
```
|
12
build/entitlements.mac.plist
Normal file
12
build/entitlements.mac.plist
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
BIN
build/icon.icns
Normal file
BIN
build/icon.icns
Normal file
Binary file not shown.
BIN
build/icon.ico
Normal file
BIN
build/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
BIN
build/icon.png
Normal file
BIN
build/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
31
commitlint.config.cjs
Normal file
31
commitlint.config.cjs
Normal file
@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
// 校验规则
|
||||
rules: {
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat',
|
||||
'fix',
|
||||
'docs',
|
||||
'style',
|
||||
'refactor',
|
||||
'perf',
|
||||
'test',
|
||||
'chore',
|
||||
'revert',
|
||||
'build',
|
||||
'init'
|
||||
]
|
||||
],
|
||||
'type-case': [0],
|
||||
'type-empty': [0],
|
||||
'scope-empty': [0],
|
||||
'scope-case': [0],
|
||||
'subject-full-stop': [0, 'never'],
|
||||
'subject-case': [0, 'never'],
|
||||
'header-max-length': [0, 'always', 72],
|
||||
'body-max-line-length': [0, 'never']
|
||||
}
|
||||
};
|
3
dev-app-update.yml
Normal file
3
dev-app-update.yml
Normal file
@ -0,0 +1,3 @@
|
||||
provider: generic
|
||||
url: https://example.com/auto-updates
|
||||
updaterCacheDirName: dsz-exam-composer-updater
|
45
electron-builder.yml
Normal file
45
electron-builder.yml
Normal file
@ -0,0 +1,45 @@
|
||||
appId: com.electron.app
|
||||
productName: dsz-exam-composer
|
||||
directories:
|
||||
buildResources: build
|
||||
files:
|
||||
- '!**/.vscode/*'
|
||||
- '!src/*'
|
||||
- '!electron.vite.config.{js,ts,mjs,cjs}'
|
||||
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
|
||||
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
|
||||
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
|
||||
asarUnpack:
|
||||
- resources/**
|
||||
win:
|
||||
executableName: dsz-exam-composer
|
||||
nsis:
|
||||
artifactName: ${name}-${version}-setup.${ext}
|
||||
shortcutName: ${productName}
|
||||
uninstallDisplayName: ${productName}
|
||||
createDesktopShortcut: always
|
||||
mac:
|
||||
entitlementsInherit: build/entitlements.mac.plist
|
||||
extendInfo:
|
||||
- NSCameraUsageDescription: Application requests access to the device's camera.
|
||||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
||||
notarize: false
|
||||
dmg:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
linux:
|
||||
target:
|
||||
- AppImage
|
||||
- snap
|
||||
- deb
|
||||
maintainer: electronjs.org
|
||||
category: Utility
|
||||
appImage:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
npmRebuild: false
|
||||
publish:
|
||||
provider: generic
|
||||
url: https://example.com/auto-updates
|
||||
electronDownload:
|
||||
mirror: https://npmmirror.com/mirrors/electron/
|
96
electron.vite.config.ts
Normal file
96
electron.vite.config.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite';
|
||||
// Plugins
|
||||
import AutoImport from 'unplugin-auto-import/vite';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
import Fonts from 'unplugin-fonts/vite';
|
||||
import Layouts from 'vite-plugin-vue-layouts';
|
||||
import Vue from '@vitejs/plugin-vue';
|
||||
import VueRouter from 'unplugin-vue-router/vite';
|
||||
// import rawLoader from 'raw-loader'
|
||||
|
||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||
|
||||
// import vue from '@vitejs/plugin-vue'
|
||||
|
||||
export default defineConfig({
|
||||
main: {
|
||||
plugins: [externalizeDepsPlugin()]
|
||||
},
|
||||
preload: {
|
||||
plugins: [externalizeDepsPlugin()]
|
||||
},
|
||||
renderer: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@renderer': resolve('src/renderer/src')
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
VueRouter({
|
||||
dts: resolve('src/renderer/src/typed-router.d.ts'),
|
||||
routesFolder: [
|
||||
{
|
||||
src: resolve('src/renderer/src/pages'),
|
||||
path: '',
|
||||
// override globals
|
||||
exclude: (excluded) => excluded,
|
||||
filePatterns: (filePatterns) => filePatterns,
|
||||
extensions: (extensions) => extensions
|
||||
}
|
||||
]
|
||||
}),
|
||||
Layouts(),
|
||||
Vue(),
|
||||
// https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme
|
||||
Components({
|
||||
dts: 'src/components.d.ts',
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Fonts({
|
||||
google: {
|
||||
families: [
|
||||
{
|
||||
name: 'Roboto',
|
||||
styles: 'wght@100;300;400;500;700;900'
|
||||
}
|
||||
]
|
||||
}
|
||||
}),
|
||||
AutoImport({
|
||||
imports: [
|
||||
'vue',
|
||||
{
|
||||
'vue-router/auto': ['useRoute', 'useRouter']
|
||||
}
|
||||
],
|
||||
dts: 'src/auto-imports.d.ts',
|
||||
eslintrc: {
|
||||
enabled: true
|
||||
},
|
||||
vueTemplate: true,
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
createSvgIconsPlugin({
|
||||
// Specify the icon folder to be cached
|
||||
iconDirs: [resolve('src/renderer/src/icons')],
|
||||
// Specify symbolId format
|
||||
symbolId: 'icon-[dir]-[name]',
|
||||
|
||||
/**
|
||||
* custom insert position
|
||||
* @default: body-last
|
||||
*/
|
||||
// inject?: 'body-last' | 'body-first',
|
||||
|
||||
/**
|
||||
* custom dom id
|
||||
* @default: __svg__icons__dom__
|
||||
*/
|
||||
customDomId: '__svg__icons__dom__'
|
||||
})
|
||||
],
|
||||
define: { 'process.env': {} }
|
||||
}
|
||||
});
|
17
examples/config.json
Normal file
17
examples/config.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"examName": "2023-2024学年度期末考试",
|
||||
"roomName": "第一考场",
|
||||
"message": "诚信考试,禁止作弊",
|
||||
"examInfos": [
|
||||
{
|
||||
"name": "语文",
|
||||
"start": "2024-07-26T15:00:00",
|
||||
"end": "2024-07-26T16:00:00"
|
||||
},
|
||||
{
|
||||
"name": "数学",
|
||||
"start": "2024-07-26T16:10:00",
|
||||
"end": "2024-07-26T17:10:00"
|
||||
}
|
||||
]
|
||||
}
|
76
package.json
Normal file
76
package.json
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "dsz-exam-showboard",
|
||||
"version": "1.0.0",
|
||||
"description": "An Electron application with Vue",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "Hello8693 <hello8693@hello8693.xyz>",
|
||||
"homepage": "https://dsz.hello8693.xyz/",
|
||||
"scripts": {
|
||||
"preinstall": "node -e \"if (process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('Use yarn for installing: https://yarnpkg.com/en/docs/install')\"",
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix",
|
||||
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
|
||||
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false",
|
||||
"typecheck": "npm run typecheck:node && npm run typecheck:web",
|
||||
"start": "electron-vite preview",
|
||||
"dev": "electron-vite dev",
|
||||
"build": "electron-vite build",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"build:unpack": "npm run build && electron-builder --dir",
|
||||
"build:win": "npm run build && electron-builder --win",
|
||||
"build:mac": "npm run build && electron-builder --mac",
|
||||
"build:linux": "npm run build && electron-builder --linux",
|
||||
"electron:generate-icons": "electron-icon-builder --input=./resources/icon.png --output=build --flatten",
|
||||
"commitlint": "commitlint --config commitlint.config.cjs -e -V",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron-toolkit/preload": "^3.0.0",
|
||||
"@electron-toolkit/utils": "^3.0.0",
|
||||
"core-js": "^3.34.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"element-plus": "^2.7.7",
|
||||
"github-markdown-css": "^5.5.1",
|
||||
"moment": "^2.30.1",
|
||||
"roboto-fontface": "*",
|
||||
"uuid": "^9.0.1",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-layouts": "^0.11.0",
|
||||
"vue": "^3.4.34",
|
||||
"vue-showdown": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@electron-toolkit/eslint-config": "^1.0.1",
|
||||
"@electron-toolkit/eslint-config-ts": "^1.0.1",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@mdi/font": "7.0.96",
|
||||
"@rushstack/eslint-patch": "^1.6.1",
|
||||
"@types/vue": "^2.0.0",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"commitlint": "^19.3.0",
|
||||
"core-js": "^3.34.0",
|
||||
"electron": "^28.2.0",
|
||||
"electron-builder": "^24.9.1",
|
||||
"electron-vite": "^2.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-vue": "^9.20.1",
|
||||
"husky": "^9.0.11",
|
||||
"pinia": "^2.1.7",
|
||||
"prettier": "^3.2.4",
|
||||
"raw-loader": "^4.0.2",
|
||||
"roboto-fontface": "*",
|
||||
"sass": "^1.71.1",
|
||||
"unplugin-auto-import": "^0.18.2",
|
||||
"unplugin-fonts": "^1.1.1",
|
||||
"unplugin-vue-components": "^0.27.3",
|
||||
"unplugin-vue-router": "^0.8.4",
|
||||
"vite": "^5.0.12",
|
||||
"vite-plugin-optimizer": "^1.4.3",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-layouts": "^0.11.0",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue-tsc": "^1.8.27"
|
||||
}
|
||||
}
|
4
pre-commit
Executable file
4
pre-commit
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
yarn commitlint
|
BIN
resources/icon.png
Normal file
BIN
resources/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
77
src/main/index.ts
Normal file
77
src/main/index.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { app } from 'electron';
|
||||
import { electronApp } from '@electron-toolkit/utils';
|
||||
import windowManager from './utils/windowManager';
|
||||
import ipcWindow from './ipc/window';
|
||||
|
||||
// function createWindow(): void {
|
||||
// // Create the browser window.
|
||||
// const mainWindow = new BrowserWindow({
|
||||
// width: 900,
|
||||
// height: 670,
|
||||
// show: false,
|
||||
// autoHideMenuBar: true,
|
||||
// ...(process.platform === 'linux' ? { icon } : {}),
|
||||
// webPreferences: {
|
||||
// preload: join(__dirname, '../preload/index.js'),
|
||||
// sandbox: false
|
||||
// }
|
||||
// })
|
||||
|
||||
// mainWindow.on('ready-to-show', () => {
|
||||
// mainWindow.show()
|
||||
// })
|
||||
|
||||
// mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||
// shell.openExternal(details.url)
|
||||
// return { action: 'deny' }
|
||||
// })
|
||||
|
||||
// // HMR for renderer base on electron-vite cli.
|
||||
// // Load the remote URL for development or the local html file for production.
|
||||
// if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
// mainWindow.loadURL(`${process.env['ELECTRON_RENDERER_URL']}/#mainWindow`)
|
||||
// } else {
|
||||
// mainWindow.loadFile(join(__dirname, '../renderer/index.html'), { hash: 'mainWindow' })
|
||||
// }
|
||||
// }
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
// app.whenReady().then(() => {
|
||||
// // Set app user model id for windows
|
||||
// electronApp.setAppUserModelId('com.electron')
|
||||
|
||||
// // Default open or close DevTools by F12 in development
|
||||
// // and ignore CommandOrControl + R in production.
|
||||
// // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||
// app.on('browser-window-created', (_, window) => {
|
||||
// optimizer.watchWindowShortcuts(window)
|
||||
// })
|
||||
|
||||
// // IPC test
|
||||
// ipcMain.on('ping', () => console.log('pong'))
|
||||
|
||||
// createWindow()
|
||||
|
||||
// app.on('activate', function () {
|
||||
// // On macOS it's common to re-create a window in the app when the
|
||||
// // dock icon is clicked and there are no other windows open.
|
||||
// if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
// })
|
||||
// })
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
// Set app user model id for windows
|
||||
electronApp.setAppUserModelId('com.electron');
|
||||
ipcWindow.register();
|
||||
const wallPaperWindow = windowManager.createMainWindow();
|
||||
// wallPaperWindow.show()
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
// In this file you can include the rest of your app"s specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
34
src/main/ipc/window.ts
Normal file
34
src/main/ipc/window.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { ipcMain, BrowserWindow, app } from 'electron';
|
||||
import windowManager from '../utils/windowManager';
|
||||
import WindowName from '../types/WindowName';
|
||||
import { fileUtils } from '../utils/fileManager';
|
||||
|
||||
const ipcWindow = {
|
||||
register() {
|
||||
ipcMain.on('window:minimize', (event) => {
|
||||
const window = BrowserWindow.fromId(event.sender.id);
|
||||
if (window) {
|
||||
// 添加此行检查window是否为null
|
||||
window.minimize();
|
||||
} else {
|
||||
console.error('Failed to minimize: Window not found');
|
||||
}
|
||||
});
|
||||
ipcMain.on('prog:exit', (event) => {
|
||||
windowManager.destroyAllWindows();
|
||||
app.quit();
|
||||
});
|
||||
ipcMain.on('window:open', (event, name: WindowName) => {
|
||||
return windowManager.createByName(name);
|
||||
});
|
||||
|
||||
ipcMain.on('prog:loadjson', async (event) => {
|
||||
const window = BrowserWindow.fromId(event.sender.id);
|
||||
const file = await fileUtils.readFile()
|
||||
await window.webContents.send('common:openFile', file);
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
export default ipcWindow;
|
3
src/main/types/WindowName.d.ts
vendored
Normal file
3
src/main/types/WindowName.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
type WindowName = 'mainWindow';
|
||||
|
||||
export default WindowName;
|
26
src/main/utils/fileManager.ts
Normal file
26
src/main/utils/fileManager.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import * as fs from 'fs/promises';
|
||||
import { dialog } from 'electron';
|
||||
|
||||
/**
|
||||
* 文件读取返回的类型,包括路径和数据
|
||||
*/
|
||||
export interface IFileInfo {
|
||||
filePath?: string;
|
||||
data: string;
|
||||
}
|
||||
/**
|
||||
* 文件操作工具类
|
||||
*/
|
||||
export const fileUtils = {
|
||||
/**
|
||||
* 打开文件,并读取文件的text
|
||||
* @returns 读取到的text文件
|
||||
*/
|
||||
readFile: async (): Promise<IFileInfo> => {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({});
|
||||
let filePath = '';
|
||||
if (!canceled) filePath = filePaths[0];
|
||||
const data = await fs.readFile(filePath);
|
||||
return { filePath, data: data.toString() };
|
||||
}
|
||||
};
|
79
src/main/utils/windowManager.ts
Normal file
79
src/main/utils/windowManager.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { BrowserWindow, BrowserWindowConstructorOptions, shell } from 'electron';
|
||||
import path from 'path';
|
||||
import WindowName from '../types/WindowName';
|
||||
import icon from '/resources/icon.png?asset';
|
||||
import { join } from 'path';
|
||||
import { is } from '@electron-toolkit/utils';
|
||||
|
||||
class WindowManager {
|
||||
// 这些都设置成 private 了,要用这些窗口的时候用下面那些 create 的方法,会返回需要的窗口,这样保证它们都是存在的
|
||||
private MainWindow?: BrowserWindow;
|
||||
|
||||
private createWindow(
|
||||
route: string,
|
||||
options: Partial<BrowserWindowConstructorOptions> = {},
|
||||
onReadyToShow?: () => any
|
||||
) {
|
||||
const win = new BrowserWindow({
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
...(process.platform === 'linux' ? { icon } : {}),
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.js'),
|
||||
sandbox: false
|
||||
},
|
||||
...options
|
||||
});
|
||||
|
||||
win.once('ready-to-show', () => {
|
||||
onReadyToShow && onReadyToShow();
|
||||
win.show();
|
||||
});
|
||||
|
||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||
// 🚧 Use ['ENV_NAME'] avoid vite:define plugin
|
||||
const url = process.env['ELECTRON_RENDERER_URL'];
|
||||
|
||||
win.loadURL(`${url}#/${route}`);
|
||||
} else {
|
||||
win.loadFile(path.resolve(__dirname, '../renderer/index.html'), { hash: route });
|
||||
}
|
||||
|
||||
// Make all links open with the browser, not with the application
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith('https:')) shell.openExternal(url);
|
||||
return { action: 'deny' };
|
||||
});
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
public createMainWindow() {
|
||||
if (this.MainWindow && !this.MainWindow.isDestroyed()) {
|
||||
this.MainWindow.show();
|
||||
return this.MainWindow;
|
||||
}
|
||||
this.MainWindow = this.createWindow('mainWindow', {
|
||||
title: 'DSZ ExamComposer 制卷系统'
|
||||
});
|
||||
this.MainWindow.on('close', () => {
|
||||
this.MainWindow = undefined;
|
||||
});
|
||||
return this.MainWindow;
|
||||
}
|
||||
|
||||
public destroyAllWindows() {
|
||||
if (this.MainWindow) {
|
||||
this.MainWindow.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public createByName(name: WindowName) {
|
||||
switch (name) {
|
||||
case 'mainWindow':
|
||||
return this.createMainWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new WindowManager();
|
8
src/preload/index.d.ts
vendored
Normal file
8
src/preload/index.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
import { ElectronAPI } from '@electron-toolkit/preload';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electron: ElectronAPI;
|
||||
api: unknown;
|
||||
}
|
||||
}
|
22
src/preload/index.ts
Normal file
22
src/preload/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { contextBridge } from 'electron';
|
||||
import { electronAPI } from '@electron-toolkit/preload';
|
||||
|
||||
// Custom APIs for renderer
|
||||
const api = {};
|
||||
|
||||
// Use `contextBridge` APIs to expose Electron APIs to
|
||||
// renderer only if context isolation is enabled, otherwise
|
||||
// just add to the DOM global.
|
||||
if (process.contextIsolated) {
|
||||
try {
|
||||
contextBridge.exposeInMainWorld('electron', electronAPI);
|
||||
contextBridge.exposeInMainWorld('api', api);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore (define in dts)
|
||||
window.electron = electronAPI;
|
||||
// @ts-ignore (define in dts)
|
||||
window.api = api;
|
||||
}
|
209
src/renderer/auto-imports.d.ts
vendored
Normal file
209
src/renderer/auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: (typeof import('vue'))['EffectScope'];
|
||||
const computed: (typeof import('vue'))['computed'];
|
||||
const createApp: (typeof import('vue'))['createApp'];
|
||||
const customRef: (typeof import('vue'))['customRef'];
|
||||
const defineAsyncComponent: (typeof import('vue'))['defineAsyncComponent'];
|
||||
const defineComponent: (typeof import('vue'))['defineComponent'];
|
||||
const effectScope: (typeof import('vue'))['effectScope'];
|
||||
const getCurrentInstance: (typeof import('vue'))['getCurrentInstance'];
|
||||
const getCurrentScope: (typeof import('vue'))['getCurrentScope'];
|
||||
const h: (typeof import('vue'))['h'];
|
||||
const inject: (typeof import('vue'))['inject'];
|
||||
const isProxy: (typeof import('vue'))['isProxy'];
|
||||
const isReactive: (typeof import('vue'))['isReactive'];
|
||||
const isReadonly: (typeof import('vue'))['isReadonly'];
|
||||
const isRef: (typeof import('vue'))['isRef'];
|
||||
const markRaw: (typeof import('vue'))['markRaw'];
|
||||
const nextTick: (typeof import('vue'))['nextTick'];
|
||||
const onActivated: (typeof import('vue'))['onActivated'];
|
||||
const onBeforeMount: (typeof import('vue'))['onBeforeMount'];
|
||||
const onBeforeRouteLeave: (typeof import('vue-router'))['onBeforeRouteLeave'];
|
||||
const onBeforeRouteUpdate: (typeof import('vue-router'))['onBeforeRouteUpdate'];
|
||||
const onBeforeUnmount: (typeof import('vue'))['onBeforeUnmount'];
|
||||
const onBeforeUpdate: (typeof import('vue'))['onBeforeUpdate'];
|
||||
const onDeactivated: (typeof import('vue'))['onDeactivated'];
|
||||
const onErrorCaptured: (typeof import('vue'))['onErrorCaptured'];
|
||||
const onMounted: (typeof import('vue'))['onMounted'];
|
||||
const onRenderTracked: (typeof import('vue'))['onRenderTracked'];
|
||||
const onRenderTriggered: (typeof import('vue'))['onRenderTriggered'];
|
||||
const onScopeDispose: (typeof import('vue'))['onScopeDispose'];
|
||||
const onServerPrefetch: (typeof import('vue'))['onServerPrefetch'];
|
||||
const onUnmounted: (typeof import('vue'))['onUnmounted'];
|
||||
const onUpdated: (typeof import('vue'))['onUpdated'];
|
||||
const provide: (typeof import('vue'))['provide'];
|
||||
const reactive: (typeof import('vue'))['reactive'];
|
||||
const readonly: (typeof import('vue'))['readonly'];
|
||||
const ref: (typeof import('vue'))['ref'];
|
||||
const resolveComponent: (typeof import('vue'))['resolveComponent'];
|
||||
const shallowReactive: (typeof import('vue'))['shallowReactive'];
|
||||
const shallowReadonly: (typeof import('vue'))['shallowReadonly'];
|
||||
const shallowRef: (typeof import('vue'))['shallowRef'];
|
||||
const toRaw: (typeof import('vue'))['toRaw'];
|
||||
const toRef: (typeof import('vue'))['toRef'];
|
||||
const toRefs: (typeof import('vue'))['toRefs'];
|
||||
const toValue: (typeof import('vue'))['toValue'];
|
||||
const triggerRef: (typeof import('vue'))['triggerRef'];
|
||||
const unref: (typeof import('vue'))['unref'];
|
||||
const useAttrs: (typeof import('vue'))['useAttrs'];
|
||||
const useCssModule: (typeof import('vue'))['useCssModule'];
|
||||
const useCssVars: (typeof import('vue'))['useCssVars'];
|
||||
const useLink: (typeof import('vue-router'))['useLink'];
|
||||
const useRoute: (typeof import('vue-router'))['useRoute'];
|
||||
const useRouter: (typeof import('vue-router'))['useRouter'];
|
||||
const useSlots: (typeof import('vue'))['useSlots'];
|
||||
const watch: (typeof import('vue'))['watch'];
|
||||
const watchEffect: (typeof import('vue'))['watchEffect'];
|
||||
const watchPostEffect: (typeof import('vue'))['watchPostEffect'];
|
||||
const watchSyncEffect: (typeof import('vue'))['watchSyncEffect'];
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type {
|
||||
Component,
|
||||
ComponentPublicInstance,
|
||||
ComputedRef,
|
||||
ExtractDefaultPropTypes,
|
||||
ExtractPropTypes,
|
||||
ExtractPublicPropTypes,
|
||||
InjectionKey,
|
||||
PropType,
|
||||
Ref,
|
||||
VNode,
|
||||
WritableComputedRef
|
||||
} from 'vue';
|
||||
import('vue');
|
||||
}
|
||||
// for vue template auto import
|
||||
import { UnwrapRef } from 'vue';
|
||||
declare module 'vue' {
|
||||
interface GlobalComponents {}
|
||||
interface ComponentCustomProperties {
|
||||
readonly EffectScope: UnwrapRef<(typeof import('vue'))['EffectScope']>;
|
||||
readonly computed: UnwrapRef<(typeof import('vue'))['computed']>;
|
||||
readonly createApp: UnwrapRef<(typeof import('vue'))['createApp']>;
|
||||
readonly customRef: UnwrapRef<(typeof import('vue'))['customRef']>;
|
||||
readonly defineAsyncComponent: UnwrapRef<(typeof import('vue'))['defineAsyncComponent']>;
|
||||
readonly defineComponent: UnwrapRef<(typeof import('vue'))['defineComponent']>;
|
||||
readonly effectScope: UnwrapRef<(typeof import('vue'))['effectScope']>;
|
||||
readonly getCurrentInstance: UnwrapRef<(typeof import('vue'))['getCurrentInstance']>;
|
||||
readonly getCurrentScope: UnwrapRef<(typeof import('vue'))['getCurrentScope']>;
|
||||
readonly h: UnwrapRef<(typeof import('vue'))['h']>;
|
||||
readonly inject: UnwrapRef<(typeof import('vue'))['inject']>;
|
||||
readonly isProxy: UnwrapRef<(typeof import('vue'))['isProxy']>;
|
||||
readonly isReactive: UnwrapRef<(typeof import('vue'))['isReactive']>;
|
||||
readonly isReadonly: UnwrapRef<(typeof import('vue'))['isReadonly']>;
|
||||
readonly isRef: UnwrapRef<(typeof import('vue'))['isRef']>;
|
||||
readonly markRaw: UnwrapRef<(typeof import('vue'))['markRaw']>;
|
||||
readonly nextTick: UnwrapRef<(typeof import('vue'))['nextTick']>;
|
||||
readonly onActivated: UnwrapRef<(typeof import('vue'))['onActivated']>;
|
||||
readonly onBeforeMount: UnwrapRef<(typeof import('vue'))['onBeforeMount']>;
|
||||
readonly onBeforeRouteLeave: UnwrapRef<(typeof import('vue-router'))['onBeforeRouteLeave']>;
|
||||
readonly onBeforeRouteUpdate: UnwrapRef<(typeof import('vue-router'))['onBeforeRouteUpdate']>;
|
||||
readonly onBeforeUnmount: UnwrapRef<(typeof import('vue'))['onBeforeUnmount']>;
|
||||
readonly onBeforeUpdate: UnwrapRef<(typeof import('vue'))['onBeforeUpdate']>;
|
||||
readonly onDeactivated: UnwrapRef<(typeof import('vue'))['onDeactivated']>;
|
||||
readonly onErrorCaptured: UnwrapRef<(typeof import('vue'))['onErrorCaptured']>;
|
||||
readonly onMounted: UnwrapRef<(typeof import('vue'))['onMounted']>;
|
||||
readonly onRenderTracked: UnwrapRef<(typeof import('vue'))['onRenderTracked']>;
|
||||
readonly onRenderTriggered: UnwrapRef<(typeof import('vue'))['onRenderTriggered']>;
|
||||
readonly onScopeDispose: UnwrapRef<(typeof import('vue'))['onScopeDispose']>;
|
||||
readonly onServerPrefetch: UnwrapRef<(typeof import('vue'))['onServerPrefetch']>;
|
||||
readonly onUnmounted: UnwrapRef<(typeof import('vue'))['onUnmounted']>;
|
||||
readonly onUpdated: UnwrapRef<(typeof import('vue'))['onUpdated']>;
|
||||
readonly provide: UnwrapRef<(typeof import('vue'))['provide']>;
|
||||
readonly reactive: UnwrapRef<(typeof import('vue'))['reactive']>;
|
||||
readonly readonly: UnwrapRef<(typeof import('vue'))['readonly']>;
|
||||
readonly ref: UnwrapRef<(typeof import('vue'))['ref']>;
|
||||
readonly resolveComponent: UnwrapRef<(typeof import('vue'))['resolveComponent']>;
|
||||
readonly shallowReactive: UnwrapRef<(typeof import('vue'))['shallowReactive']>;
|
||||
readonly shallowReadonly: UnwrapRef<(typeof import('vue'))['shallowReadonly']>;
|
||||
readonly shallowRef: UnwrapRef<(typeof import('vue'))['shallowRef']>;
|
||||
readonly toRaw: UnwrapRef<(typeof import('vue'))['toRaw']>;
|
||||
readonly toRef: UnwrapRef<(typeof import('vue'))['toRef']>;
|
||||
readonly toRefs: UnwrapRef<(typeof import('vue'))['toRefs']>;
|
||||
readonly toValue: UnwrapRef<(typeof import('vue'))['toValue']>;
|
||||
readonly triggerRef: UnwrapRef<(typeof import('vue'))['triggerRef']>;
|
||||
readonly unref: UnwrapRef<(typeof import('vue'))['unref']>;
|
||||
readonly useAttrs: UnwrapRef<(typeof import('vue'))['useAttrs']>;
|
||||
readonly useCssModule: UnwrapRef<(typeof import('vue'))['useCssModule']>;
|
||||
readonly useCssVars: UnwrapRef<(typeof import('vue'))['useCssVars']>;
|
||||
readonly useLink: UnwrapRef<(typeof import('vue-router'))['useLink']>;
|
||||
readonly useRoute: UnwrapRef<(typeof import('vue-router'))['useRoute']>;
|
||||
readonly useRouter: UnwrapRef<(typeof import('vue-router'))['useRouter']>;
|
||||
readonly useSlots: UnwrapRef<(typeof import('vue'))['useSlots']>;
|
||||
readonly watch: UnwrapRef<(typeof import('vue'))['watch']>;
|
||||
readonly watchEffect: UnwrapRef<(typeof import('vue'))['watchEffect']>;
|
||||
readonly watchPostEffect: UnwrapRef<(typeof import('vue'))['watchPostEffect']>;
|
||||
readonly watchSyncEffect: UnwrapRef<(typeof import('vue'))['watchSyncEffect']>;
|
||||
}
|
||||
}
|
||||
declare module '@vue/runtime-core' {
|
||||
interface GlobalComponents {}
|
||||
interface ComponentCustomProperties {
|
||||
readonly EffectScope: UnwrapRef<(typeof import('vue'))['EffectScope']>;
|
||||
readonly computed: UnwrapRef<(typeof import('vue'))['computed']>;
|
||||
readonly createApp: UnwrapRef<(typeof import('vue'))['createApp']>;
|
||||
readonly customRef: UnwrapRef<(typeof import('vue'))['customRef']>;
|
||||
readonly defineAsyncComponent: UnwrapRef<(typeof import('vue'))['defineAsyncComponent']>;
|
||||
readonly defineComponent: UnwrapRef<(typeof import('vue'))['defineComponent']>;
|
||||
readonly effectScope: UnwrapRef<(typeof import('vue'))['effectScope']>;
|
||||
readonly getCurrentInstance: UnwrapRef<(typeof import('vue'))['getCurrentInstance']>;
|
||||
readonly getCurrentScope: UnwrapRef<(typeof import('vue'))['getCurrentScope']>;
|
||||
readonly h: UnwrapRef<(typeof import('vue'))['h']>;
|
||||
readonly inject: UnwrapRef<(typeof import('vue'))['inject']>;
|
||||
readonly isProxy: UnwrapRef<(typeof import('vue'))['isProxy']>;
|
||||
readonly isReactive: UnwrapRef<(typeof import('vue'))['isReactive']>;
|
||||
readonly isReadonly: UnwrapRef<(typeof import('vue'))['isReadonly']>;
|
||||
readonly isRef: UnwrapRef<(typeof import('vue'))['isRef']>;
|
||||
readonly markRaw: UnwrapRef<(typeof import('vue'))['markRaw']>;
|
||||
readonly nextTick: UnwrapRef<(typeof import('vue'))['nextTick']>;
|
||||
readonly onActivated: UnwrapRef<(typeof import('vue'))['onActivated']>;
|
||||
readonly onBeforeMount: UnwrapRef<(typeof import('vue'))['onBeforeMount']>;
|
||||
readonly onBeforeRouteLeave: UnwrapRef<(typeof import('vue-router'))['onBeforeRouteLeave']>;
|
||||
readonly onBeforeRouteUpdate: UnwrapRef<(typeof import('vue-router'))['onBeforeRouteUpdate']>;
|
||||
readonly onBeforeUnmount: UnwrapRef<(typeof import('vue'))['onBeforeUnmount']>;
|
||||
readonly onBeforeUpdate: UnwrapRef<(typeof import('vue'))['onBeforeUpdate']>;
|
||||
readonly onDeactivated: UnwrapRef<(typeof import('vue'))['onDeactivated']>;
|
||||
readonly onErrorCaptured: UnwrapRef<(typeof import('vue'))['onErrorCaptured']>;
|
||||
readonly onMounted: UnwrapRef<(typeof import('vue'))['onMounted']>;
|
||||
readonly onRenderTracked: UnwrapRef<(typeof import('vue'))['onRenderTracked']>;
|
||||
readonly onRenderTriggered: UnwrapRef<(typeof import('vue'))['onRenderTriggered']>;
|
||||
readonly onScopeDispose: UnwrapRef<(typeof import('vue'))['onScopeDispose']>;
|
||||
readonly onServerPrefetch: UnwrapRef<(typeof import('vue'))['onServerPrefetch']>;
|
||||
readonly onUnmounted: UnwrapRef<(typeof import('vue'))['onUnmounted']>;
|
||||
readonly onUpdated: UnwrapRef<(typeof import('vue'))['onUpdated']>;
|
||||
readonly provide: UnwrapRef<(typeof import('vue'))['provide']>;
|
||||
readonly reactive: UnwrapRef<(typeof import('vue'))['reactive']>;
|
||||
readonly readonly: UnwrapRef<(typeof import('vue'))['readonly']>;
|
||||
readonly ref: UnwrapRef<(typeof import('vue'))['ref']>;
|
||||
readonly resolveComponent: UnwrapRef<(typeof import('vue'))['resolveComponent']>;
|
||||
readonly shallowReactive: UnwrapRef<(typeof import('vue'))['shallowReactive']>;
|
||||
readonly shallowReadonly: UnwrapRef<(typeof import('vue'))['shallowReadonly']>;
|
||||
readonly shallowRef: UnwrapRef<(typeof import('vue'))['shallowRef']>;
|
||||
readonly toRaw: UnwrapRef<(typeof import('vue'))['toRaw']>;
|
||||
readonly toRef: UnwrapRef<(typeof import('vue'))['toRef']>;
|
||||
readonly toRefs: UnwrapRef<(typeof import('vue'))['toRefs']>;
|
||||
readonly toValue: UnwrapRef<(typeof import('vue'))['toValue']>;
|
||||
readonly triggerRef: UnwrapRef<(typeof import('vue'))['triggerRef']>;
|
||||
readonly unref: UnwrapRef<(typeof import('vue'))['unref']>;
|
||||
readonly useAttrs: UnwrapRef<(typeof import('vue'))['useAttrs']>;
|
||||
readonly useCssModule: UnwrapRef<(typeof import('vue'))['useCssModule']>;
|
||||
readonly useCssVars: UnwrapRef<(typeof import('vue'))['useCssVars']>;
|
||||
readonly useLink: UnwrapRef<(typeof import('vue-router'))['useLink']>;
|
||||
readonly useRoute: UnwrapRef<(typeof import('vue-router'))['useRoute']>;
|
||||
readonly useRouter: UnwrapRef<(typeof import('vue-router'))['useRouter']>;
|
||||
readonly useSlots: UnwrapRef<(typeof import('vue'))['useSlots']>;
|
||||
readonly watch: UnwrapRef<(typeof import('vue'))['watch']>;
|
||||
readonly watchEffect: UnwrapRef<(typeof import('vue'))['watchEffect']>;
|
||||
readonly watchPostEffect: UnwrapRef<(typeof import('vue'))['watchPostEffect']>;
|
||||
readonly watchSyncEffect: UnwrapRef<(typeof import('vue'))['watchSyncEffect']>;
|
||||
}
|
||||
}
|
15
src/renderer/components.d.ts
vendored
Normal file
15
src/renderer/components.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AppFooter: (typeof import('./src/components/AppFooter.vue'))['default'];
|
||||
HelloWorld: (typeof import('./src/components/HelloWorld.vue'))['default'];
|
||||
RouterLink: (typeof import('vue-router'))['RouterLink'];
|
||||
RouterView: (typeof import('vue-router'))['RouterView'];
|
||||
}
|
||||
}
|
14
src/renderer/index.html
Normal file
14
src/renderer/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>DSZ Exam Showboard</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
10
src/renderer/src/App.vue
Normal file
10
src/renderer/src/App.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<main>
|
||||
<router-view />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const router = useRouter();
|
||||
console.log(router.options.routes);
|
||||
</script>
|
BIN
src/renderer/src/assets/logo.png
Normal file
BIN
src/renderer/src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
6
src/renderer/src/assets/logo.svg
Normal file
6
src/renderer/src/assets/logo.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M261.126 140.65L164.624 307.732L256.001 466L377.028 256.5L498.001 47H315.192L261.126 140.65Z" fill="#1697F6"/>
|
||||
<path d="M135.027 256.5L141.365 267.518L231.64 111.178L268.731 47H256H14L135.027 256.5Z" fill="#AEDDFF"/>
|
||||
<path d="M315.191 47C360.935 197.446 256 466 256 466L164.624 307.732L315.191 47Z" fill="#1867C0"/>
|
||||
<path d="M268.731 47C76.0026 47 141.366 267.518 141.366 267.518L268.731 47Z" fill="#7BC6FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 526 B |
188
src/renderer/src/auto-imports.d.ts
vendored
Normal file
188
src/renderer/src/auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const customRef: typeof import('vue')['customRef']
|
||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||
const defineComponent: typeof import('vue')['defineComponent']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const h: typeof import('vue')['h']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||
const onMounted: typeof import('vue')['onMounted']
|
||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const reactive: typeof import('vue')['reactive']
|
||||
const readonly: typeof import('vue')['readonly']
|
||||
const ref: typeof import('vue')['ref']
|
||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const toValue: typeof import('vue')['toValue']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const unref: typeof import('vue')['unref']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
const useCssVars: typeof import('vue')['useCssVars']
|
||||
const useRoute: typeof import('vue-router/auto')['useRoute']
|
||||
const useRouter: typeof import('vue-router/auto')['useRouter']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
const watch: typeof import('vue')['watch']
|
||||
const watchEffect: typeof import('vue')['watchEffect']
|
||||
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
// for vue template auto import
|
||||
import { UnwrapRef } from 'vue'
|
||||
declare module 'vue' {
|
||||
interface GlobalComponents {}
|
||||
interface ComponentCustomProperties {
|
||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||
readonly useRoute: UnwrapRef<typeof import('vue-router/auto')['useRoute']>
|
||||
readonly useRouter: UnwrapRef<typeof import('vue-router/auto')['useRouter']>
|
||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||
}
|
||||
}
|
||||
declare module '@vue/runtime-core' {
|
||||
interface GlobalComponents {}
|
||||
interface ComponentCustomProperties {
|
||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||
readonly useRoute: UnwrapRef<typeof import('vue-router/auto')['useRoute']>
|
||||
readonly useRouter: UnwrapRef<typeof import('vue-router/auto')['useRouter']>
|
||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||
}
|
||||
}
|
30
src/renderer/src/components.d.ts
vendored
Normal file
30
src/renderer/src/components.d.ts
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AppFooter: (typeof import('./components/AppFooter.vue'))['default']
|
||||
AppTopBar: (typeof import('./components/AppTopBar.vue'))['default']
|
||||
DBtn: typeof import('vue-devui/btn/index.es.js')['Btn']
|
||||
DButton: typeof import('vue-devui/button/index.es.js')['Button']
|
||||
DCodeEditor: (typeof import('vue-devui/code-editor/index.es.js'))['CodeEditor']
|
||||
DCol: typeof import('vue-devui/grid/index.es.js')['Col']
|
||||
DRow: typeof import('vue-devui/grid/index.es.js')['Row']
|
||||
DStatus: typeof import('vue-devui/status/index.es.js')['Status']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElText: typeof import('element-plus/es')['ElText']
|
||||
ExamStatus: typeof import('./components/ExamStatus.vue')['default']
|
||||
GgbPlayer: (typeof import('./components/GgbPlayer.vue'))['default']
|
||||
ReadmeRender: (typeof import('./components/ReadmeRender.vue'))['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SubjectInfo: typeof import('./components/SubjectInfo.vue')['default']
|
||||
SvgIcon: typeof import('./components/SvgIcon.vue')['default']
|
||||
}
|
||||
}
|
66
src/renderer/src/components/ExamStatus.vue
Normal file
66
src/renderer/src/components/ExamStatus.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="exam-status">
|
||||
<el-row class="exam-info-bar">
|
||||
<el-col :span="3">
|
||||
<strong>{{ exam.name }}</strong>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
{{ formatDateTime(exam.start) }} ~ {{ formatDateTime(exam.end) }}
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<el-text class="mx-1" size="large" :type="statusColor">{{ statusText }}</el-text>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import moment from 'moment';
|
||||
|
||||
const props = defineProps({
|
||||
exam: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const formatDateTime = (isoString) => moment(isoString).format('HH:mm');
|
||||
|
||||
const statusColor = computed(() => {
|
||||
const now = moment();
|
||||
const start = moment(props.exam.start);
|
||||
const end = moment(props.exam.end);
|
||||
|
||||
if (now.isBefore(start)) return 'running';
|
||||
if (now.isBetween(start, end)) return 'success';
|
||||
if (now.isAfter(end)) return 'error';
|
||||
});
|
||||
|
||||
const statusText = computed(() => {
|
||||
const now = moment();
|
||||
const start = moment(props.exam.start);
|
||||
const end = moment(props.exam.end);
|
||||
|
||||
if (now.isBefore(start)) return '未开始';
|
||||
if (now.isBetween(start, end)) return '进行中';
|
||||
if (now.isAfter(end)) return '已结束';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.exam-status {
|
||||
|
||||
padding: 10px;
|
||||
border: 1px solid #e9e9e9;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.exam-info-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
</style>
|
50
src/renderer/src/components/SubjectInfo.vue
Normal file
50
src/renderer/src/components/SubjectInfo.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="exam-info">
|
||||
<h2>当前科目: {{ exam.name }}</h2>
|
||||
<h2>考试时间: {{ formatDateTime(exam.start) }} ~ {{ formatDateTime(exam.end) }}</h2>
|
||||
<h2>考试状态: {{ statusText }}</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import moment from 'moment';
|
||||
|
||||
const props = defineProps({
|
||||
exam: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const formatDateTime = (isoString) => moment(isoString).format('HH:mm');
|
||||
|
||||
const statusColor = computed(() => {
|
||||
const now = moment();
|
||||
const start = moment(props.exam.start);
|
||||
const end = moment(props.exam.end);
|
||||
|
||||
if (now.isBefore(start)) return 'running';
|
||||
if (now.isBetween(start, end)) return 'success';
|
||||
if (now.isAfter(end)) return 'error';
|
||||
});
|
||||
|
||||
const statusText = computed(() => {
|
||||
const now = moment();
|
||||
const start = moment(props.exam.start);
|
||||
const end = moment(props.exam.end);
|
||||
|
||||
if (now.isBefore(start)) return '未开始';
|
||||
if (now.isBetween(start, end)) return '进行中';
|
||||
if (now.isAfter(end)) return '已结束';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.exam-info-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
</style>
|
35
src/renderer/src/components/SvgIcon.vue
Normal file
35
src/renderer/src/components/SvgIcon.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<svg aria-hidden="true" :style="{ width: size, height: size }">
|
||||
<use :href="symbolId" :fill="color" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, computed } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SvgIcon',
|
||||
props: {
|
||||
prefix: {
|
||||
type: String,
|
||||
default: 'icon'
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333'
|
||||
},
|
||||
size: {
|
||||
type: [String, Number],
|
||||
default: '12px'
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const symbolId = computed(() => `#${props.prefix}-${props.name}`);
|
||||
return { symbolId };
|
||||
}
|
||||
});
|
||||
</script>
|
12
src/renderer/src/interfaces/timeTable.ts
Normal file
12
src/renderer/src/interfaces/timeTable.ts
Normal file
@ -0,0 +1,12 @@
|
||||
export interface TimeSlot {
|
||||
name: string;
|
||||
start: string;
|
||||
end: string;
|
||||
}
|
||||
|
||||
export interface ExamSchedule {
|
||||
examName: string;
|
||||
roomName: string;
|
||||
message: string;
|
||||
examInfos: TimeSlot[];
|
||||
}
|
5
src/renderer/src/layouts/README.md
Normal file
5
src/renderer/src/layouts/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Layouts
|
||||
|
||||
Layouts are reusable components that wrap around pages. They are used to provide a consistent look and feel across multiple pages.
|
||||
|
||||
Full documentation for this feature can be found in the Official [vite-plugin-vue-layouts](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) repository.
|
9
src/renderer/src/layouts/default.vue
Normal file
9
src/renderer/src/layouts/default.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<div><router-view /></div>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
//
|
||||
</script>
|
31
src/renderer/src/main.ts
Normal file
31
src/renderer/src/main.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* main.js
|
||||
*
|
||||
* Bootstraps Vuetify and other plugins then mounts the App`
|
||||
*/
|
||||
|
||||
// Plugins
|
||||
import { registerPlugins } from './plugins';
|
||||
|
||||
// Components
|
||||
import App from './App.vue';
|
||||
|
||||
// Composables
|
||||
import { createApp } from 'vue';
|
||||
|
||||
import { VueShowdownPlugin } from 'vue-showdown';
|
||||
import 'virtual:svg-icons-register';
|
||||
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(VueShowdownPlugin, {
|
||||
// 设置 showdown 默认 flavor
|
||||
flavor: 'github'
|
||||
});
|
||||
|
||||
registerPlugins(app);
|
||||
|
||||
app.mount('#app');
|
5
src/renderer/src/pages/README.md
Normal file
5
src/renderer/src/pages/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Pages
|
||||
|
||||
Vue components created in this folder will automatically be converted to navigatable routes.
|
||||
|
||||
Full documentation for this feature can be found in the Official [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) repository.
|
15
src/renderer/src/pages/index.vue
Normal file
15
src/renderer/src/pages/index.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<p>跳转首页中</p>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
onMounted(() => {
|
||||
// 当组件挂载完成后,自动跳转到'/mainWindow'
|
||||
router.push('/mainWindow');
|
||||
});
|
||||
</script>
|
52
src/renderer/src/pages/infoPage.vue
Normal file
52
src/renderer/src/pages/infoPage.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="main-area">
|
||||
<h1>{{ globalStore.examName }}</h1>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="13">
|
||||
<SubjectInfo
|
||||
v-if="isExamRunning"
|
||||
:exam="runningExam"
|
||||
></SubjectInfo>
|
||||
<h2 v-else>
|
||||
考试未开始
|
||||
</h2>
|
||||
</el-col>
|
||||
<el-col :span="11">
|
||||
<div v-for="exam in globalStore.examInfos">
|
||||
<ExamStatus :exam="exam" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useAppStore } from '@renderer/stores/app';
|
||||
import { getCurrentTimeSlot } from '@renderer/utils/subjectUtils'
|
||||
|
||||
const globalStore = useAppStore();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
getCurrentTimeSlot(globalStore.examInfos);
|
||||
|
||||
const runningExam = computed(() => {
|
||||
return getCurrentTimeSlot(globalStore.examInfos);
|
||||
});
|
||||
|
||||
const isExamRunning = computed(() =>{
|
||||
return getCurrentTimeSlot(globalStore.examInfos) == null;
|
||||
});
|
||||
|
||||
|
||||
//
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.main-area {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
</style>
|
30
src/renderer/src/pages/mainWindow.vue
Normal file
30
src/renderer/src/pages/mainWindow.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>电视猪考试看板</h1>
|
||||
|
||||
<el-button @click="OpenDialog">打开配置</el-button>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useAppStore } from '@renderer/stores/app';
|
||||
|
||||
const globalStore = useAppStore()
|
||||
|
||||
|
||||
function OpenDialog() {
|
||||
window.electron.ipcRenderer.send('prog:loadjson');
|
||||
}
|
||||
|
||||
window.electron.ipcRenderer.on('common:openFile', (event, message) => {
|
||||
console.log(message.data);
|
||||
let examdata = JSON.parse(message.data);
|
||||
globalStore.$patch(examdata);
|
||||
router.push('/infoPage');
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
//
|
||||
</script>
|
19
src/renderer/src/pages/testComponents.vue
Normal file
19
src/renderer/src/pages/testComponents.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>测试页面</h1>
|
||||
<ExamStatus
|
||||
:exam="{
|
||||
name: '语文',
|
||||
start: '2024-07-25T08:54:52.717Z',
|
||||
end: '2024-07-25T08:55:40.428Z'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
//
|
||||
</script>
|
3
src/renderer/src/plugins/README.md
Normal file
3
src/renderer/src/plugins/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Plugins
|
||||
|
||||
Plugins are a way to extend the functionality of your Vue application. Use this folder for registering plugins that you want to use globally.
|
14
src/renderer/src/plugins/index.ts
Normal file
14
src/renderer/src/plugins/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* plugins/index.js
|
||||
*
|
||||
* Automatically included in `./src/main.ts`
|
||||
*/
|
||||
|
||||
// Plugins
|
||||
import pinia from '../stores';
|
||||
import router from '../router';
|
||||
import { App } from 'vue';
|
||||
|
||||
export function registerPlugins(app: App) {
|
||||
app.use(router).use(pinia);
|
||||
}
|
16
src/renderer/src/router/index.ts
Normal file
16
src/renderer/src/router/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* router/index.ts
|
||||
*
|
||||
* Automatic routes for `./src/pages/*.vue`
|
||||
*/
|
||||
|
||||
// Composables
|
||||
import { createRouter, createWebHashHistory } from 'vue-router/auto';
|
||||
import { setupLayouts } from 'virtual:generated-layouts';
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
extendRoutes: setupLayouts
|
||||
});
|
||||
|
||||
export default router;
|
5
src/renderer/src/stores/README.md
Normal file
5
src/renderer/src/stores/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Store
|
||||
|
||||
Pinia stores are used to store reactive state and expose actions to mutate it.
|
||||
|
||||
Full documentation for this feature can be found in the Official [Pinia](https://pinia.esm.dev/) repository.
|
12
src/renderer/src/stores/app.js
Normal file
12
src/renderer/src/stores/app.js
Normal file
@ -0,0 +1,12 @@
|
||||
// Utilities
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
export const useAppStore = defineStore('app', {
|
||||
state: () => ({
|
||||
examName: '考试名称',
|
||||
|
||||
roomName: '考场名称',
|
||||
message: '考试提醒信息',
|
||||
examInfos: []
|
||||
})
|
||||
});
|
4
src/renderer/src/stores/index.js
Normal file
4
src/renderer/src/stores/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
// Utilities
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
export default createPinia();
|
3
src/renderer/src/styles/README.md
Normal file
3
src/renderer/src/styles/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Styles
|
||||
|
||||
This directory is for configuring the styles of the application.
|
10
src/renderer/src/styles/settings.scss
Normal file
10
src/renderer/src/styles/settings.scss
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* src/styles/settings.scss
|
||||
*
|
||||
* Configures SASS variables and Vuetify overwrites
|
||||
*/
|
||||
|
||||
// https://vuetifyjs.com/features/sass-variables/`
|
||||
// @use 'vuetify/settings' with (
|
||||
// $color-pack: false
|
||||
// );
|
26
src/renderer/src/typed-router.d.ts
vendored
Normal file
26
src/renderer/src/typed-router.d.ts
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-router. ‼️ DO NOT MODIFY THIS FILE ‼️
|
||||
// It's recommended to commit this file.
|
||||
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry.
|
||||
|
||||
declare module 'vue-router/auto-routes' {
|
||||
import type {
|
||||
RouteRecordInfo,
|
||||
ParamValue,
|
||||
ParamValueOneOrMore,
|
||||
ParamValueZeroOrMore,
|
||||
ParamValueZeroOrOne,
|
||||
} from 'unplugin-vue-router/types'
|
||||
|
||||
/**
|
||||
* Route name map generated by unplugin-vue-router
|
||||
*/
|
||||
export interface RouteNamedMap {
|
||||
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
||||
'/infoPage': RouteRecordInfo<'/infoPage', '/infoPage', Record<never, never>, Record<never, never>>,
|
||||
'/mainWindow': RouteRecordInfo<'/mainWindow', '/mainWindow', Record<never, never>, Record<never, never>>,
|
||||
'/testComponents': RouteRecordInfo<'/testComponents', '/testComponents', Record<never, never>, Record<never, never>>,
|
||||
}
|
||||
}
|
30
src/renderer/src/utils/subjectUtils.ts
Normal file
30
src/renderer/src/utils/subjectUtils.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { parseISO, isBefore } from 'date-fns';
|
||||
import { TimeSlot } from '@renderer/interfaces/timeTable';
|
||||
|
||||
/**
|
||||
* 获取当前时间所属的时间段。
|
||||
*
|
||||
* 该函数遍历给定的时间段数组,比较当前时间是否位于某个时间段的开始时间和结束时间之间。
|
||||
* 如果找到这样的时间段,则返回该时间段;如果没有找到,返回null。
|
||||
*
|
||||
* @param timeSlots 时间段数组,每个元素包含开始时间和结束时间。
|
||||
* @returns 当前时间所属的时间段,如果没有任何时间段包含当前时间,则返回null。
|
||||
*/
|
||||
export function getCurrentTimeSlot(timeSlots: TimeSlot[]): TimeSlot | null {
|
||||
// 获取当前日期和时间
|
||||
const now = new Date();
|
||||
// 遍历时间段数组
|
||||
for (const slot of timeSlots) {
|
||||
// 解析时间段的开始时间
|
||||
const startTime = parseISO(slot.start);
|
||||
// 解析时间段的结束时间
|
||||
const endTime = parseISO(slot.end);
|
||||
// 判断当前时间是否在开始时间和结束时间之间
|
||||
if (isBefore(startTime, now) && isBefore(now, endTime)) {
|
||||
// 如果是,返回当前时间段
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
// 如果没有找到包含当前时间的时间段,返回null
|
||||
return null;
|
||||
}
|
4
tsconfig.json
Normal file
4
tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }]
|
||||
}
|
9
tsconfig.node.json
Normal file
9
tsconfig.node.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
|
||||
"include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["electron-vite/node"],
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
37
tsconfig.web.json
Normal file
37
tsconfig.web.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
|
||||
"include": [
|
||||
"src/renderer/src/env.d.ts",
|
||||
"src/renderer/src/**/*",
|
||||
"src/renderer/src/**/*.vue",
|
||||
"src/preload/*.d.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@renderer/*": [
|
||||
"src/renderer/src/*"
|
||||
]
|
||||
},
|
||||
"target": "ESNext",
|
||||
"jsx": "preserve",
|
||||
"lib": ["DOM", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"types": [
|
||||
"vite/client",
|
||||
"vite-plugin-vue-layouts/client",
|
||||
"unplugin-vue-router/client"
|
||||
],
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user