refactor(tauri): 移除持久化存储和HTTP插件优化应用架构

- 移除了 tauri-plugin-persisted-scope 和 tauri-plugin-http 插件依赖
- 从桌面功能权限中移除了文件系统和HTTP访问权限
- 删除了相关的存储键常量定义
- 修改悬浮列表窗口创建逻辑,移除页面类型参数和动态计算功能
- 主窗口配置调整为非透明背景并设置白色背景色
- 移除了前端悬浮列表中互动课堂和智评会议相关功能组件
- 更新Cargo.lock移除相关依赖包确保构建一致性
This commit is contained in:
2026-03-15 11:32:31 +08:00
parent f795de3292
commit a3b31f0ff9
15 changed files with 167 additions and 873 deletions

View File

@ -14,6 +14,8 @@
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
overflow: hidden; overflow: hidden;
background: #fff;
border-radius: 8px;
} }
body > .html-body-app { body > .html-body-app {
margin: 0; margin: 0;
@ -115,23 +117,7 @@
} }
</style> </style>
</head> </head>
<script type="module">
import { type } from '@tauri-apps/plugin-os';
import { isTauri } from '@tauri-apps/api/core';
import { getCurrentWindow } from '@tauri-apps/api/window';
if (isTauri()) {
let currentWindow = getCurrentWindow();
if (currentWindow.label === 'main') {
document.body.querySelector('body > #app').style.background = '#ffffff';
}
document.body.style.borderRadius = '8px';
document.body.style.overflow = 'hidden';
if (currentWindow.label === 'drawing-board') {
currentWindow.show();
}
}
</script>
<body class="devtool" style="margin: 0; width: 100vw !important"> <body class="devtool" style="margin: 0; width: 100vw !important">
<div id="app" class="html-body-app"> <div id="app" class="html-body-app">
<!-- 加载中 --> <!-- 加载中 -->

View File

@ -250,6 +250,7 @@ export class MeetingWebSocketGateway implements OnGatewayConnection, OnGatewayDi
const shortUid = Number(socket.data.user?.shortUid || 0); const shortUid = Number(socket.data.user?.shortUid || 0);
const userName = socket.data.user?.userName || data.userName || '用户'; const userName = socket.data.user?.userName || data.userName || '用户';
const isHost = socket.data.user?.role !== 0; const isHost = socket.data.user?.role !== 0;
const joinMode: JoinMode = const joinMode: JoinMode =
data?.joinMode === 'classroom' || data?.joinMode === 'monitor' || data?.joinMode === 'student' ? data.joinMode : isHost ? 'classroom' : 'student'; data?.joinMode === 'classroom' || data?.joinMode === 'monitor' || data?.joinMode === 'student' ? data.joinMode : isHost ? 'classroom' : 'student';

357
src-tauri/Cargo.lock generated
View File

@ -262,15 +262,6 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.8.0" version = "0.8.0"
@ -568,57 +559,10 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
dependencies = [ dependencies = [
"percent-encoding",
"time", "time",
"version_check", "version_check",
] ]
[[package]]
name = "cookie_store"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
dependencies = [
"cookie",
"document-features",
"idna",
"log",
"publicsuffix",
"serde",
"serde_derive",
"serde_json",
"time",
"url",
]
[[package]]
name = "cookie_store"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
dependencies = [
"cookie",
"document-features",
"idna",
"log",
"publicsuffix",
"serde",
"serde_derive",
"serde_json",
"time",
"url",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.10.1" version = "0.10.1"
@ -642,7 +586,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"core-foundation 0.10.1", "core-foundation",
"core-graphics-types", "core-graphics-types",
"foreign-types", "foreign-types",
"libc", "libc",
@ -655,7 +599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"core-foundation 0.10.1", "core-foundation",
"libc", "libc",
] ]
@ -898,12 +842,6 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "data-url"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.5.8" version = "0.5.8"
@ -1067,15 +1005,6 @@ dependencies = [
"syn 2.0.117", "syn 2.0.117",
] ]
[[package]]
name = "document-features"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
dependencies = [
"litrs",
]
[[package]] [[package]]
name = "dom_query" name = "dom_query"
version = "0.25.1" version = "0.25.1"
@ -1583,10 +1512,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"wasi 0.11.1+wasi-snapshot-preview1", "wasi 0.11.1+wasi-snapshot-preview1",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -1596,11 +1523,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"r-efi 5.3.0", "r-efi 5.3.0",
"wasip2", "wasip2",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -1765,25 +1690,6 @@ dependencies = [
"syn 2.0.117", "syn 2.0.117",
] ]
[[package]]
name = "h2"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap 2.13.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@ -1900,7 +1806,6 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"h2",
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
@ -1926,7 +1831,6 @@ dependencies = [
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
"tower-service", "tower-service",
"webpki-roots 1.0.6",
] ]
[[package]] [[package]]
@ -1947,11 +1851,9 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
"windows-registry",
] ]
[[package]] [[package]]
@ -2351,12 +2253,6 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.14" version = "0.4.14"
@ -2372,12 +2268,6 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru-slab"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]] [[package]]
name = "mac" name = "mac"
version = "0.1.1" version = "0.1.1"
@ -3424,22 +3314,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "psl-types"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
[[package]]
name = "publicsuffix"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
dependencies = [
"idna",
"psl-types",
]
[[package]] [[package]]
name = "pxfm" name = "pxfm"
version = "0.1.28" version = "0.1.28"
@ -3455,61 +3329,6 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "quinn"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls",
"socket2",
"thiserror 2.0.18",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
dependencies = [
"bytes",
"getrandom 0.3.4",
"lru-slab",
"rand 0.9.2",
"ring",
"rustc-hash",
"rustls",
"rustls-pki-types",
"slab",
"thiserror 2.0.18",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.60.2",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.45" version = "1.0.45"
@ -3752,49 +3571,6 @@ version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "reqwest"
version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64 0.22.1",
"bytes",
"cookie",
"cookie_store 0.22.1",
"encoding_rs",
"futures-core",
"h2",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-util",
"js-sys",
"log",
"mime",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tokio-rustls",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots 1.0.6",
]
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.13.2" version = "0.13.2"
@ -3954,7 +3730,6 @@ version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [ dependencies = [
"web-time",
"zeroize", "zeroize",
] ]
@ -3964,7 +3739,7 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
dependencies = [ dependencies = [
"core-foundation 0.10.1", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"jni", "jni",
"log", "log",
@ -4002,12 +3777,6 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -4090,7 +3859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"core-foundation 0.10.1", "core-foundation",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
"security-framework-sys", "security-framework-sys",
@ -4248,18 +4017,6 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "serde_with" name = "serde_with"
version = "3.17.0" version = "3.17.0"
@ -4755,27 +4512,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "system-configuration"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
dependencies = [
"bitflags 2.11.0",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "6.2.2" version = "6.2.2"
@ -4797,7 +4533,7 @@ checksum = "6e06d52c379e63da659a483a958110bbde891695a0ecb53e48cc7786d5eda7bb"
dependencies = [ dependencies = [
"bitflags 2.11.0", "bitflags 2.11.0",
"block2", "block2",
"core-foundation 0.10.1", "core-foundation",
"core-graphics", "core-graphics",
"crossbeam-channel", "crossbeam-channel",
"dispatch2", "dispatch2",
@ -4886,7 +4622,7 @@ dependencies = [
"percent-encoding", "percent-encoding",
"plist", "plist",
"raw-window-handle", "raw-window-handle",
"reqwest 0.13.2", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
"serde_repr", "serde_repr",
@ -5027,30 +4763,6 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "tauri-plugin-http"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8f069451c4e87e7e2636b7f065a4c52866c4ce5e60e2d53fa1038edb6d184dc"
dependencies = [
"bytes",
"cookie_store 0.21.1",
"data-url",
"http",
"regex",
"reqwest 0.12.28",
"schemars 0.8.22",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"tauri-plugin-fs",
"thiserror 2.0.18",
"tokio",
"url",
"urlpattern",
]
[[package]] [[package]]
name = "tauri-plugin-os" name = "tauri-plugin-os"
version = "2.3.2" version = "2.3.2"
@ -5069,22 +4781,6 @@ dependencies = [
"thiserror 2.0.18", "thiserror 2.0.18",
] ]
[[package]]
name = "tauri-plugin-persisted-scope"
version = "2.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb33765b205abc72a1384d0a73489a00bae6ca1e151c5e824ac80115949d2e1"
dependencies = [
"aho-corasick",
"bincode",
"log",
"serde",
"serde_json",
"tauri",
"tauri-plugin-fs",
"thiserror 2.0.18",
]
[[package]] [[package]]
name = "tauri-plugin-process" name = "tauri-plugin-process"
version = "2.3.1" version = "2.3.1"
@ -5126,7 +4822,7 @@ dependencies = [
"minisign-verify", "minisign-verify",
"osakit", "osakit",
"percent-encoding", "percent-encoding",
"reqwest 0.13.2", "reqwest",
"rustls", "rustls",
"semver", "semver",
"serde", "serde",
@ -5368,21 +5064,6 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "tinyvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.50.0" version = "1.50.0"
@ -6028,16 +5709,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "web_atoms" name = "web_atoms"
version = "0.2.3" version = "0.2.3"
@ -6348,17 +6019,6 @@ dependencies = [
"windows-link 0.2.1", "windows-link 0.2.1",
] ]
[[package]]
name = "windows-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
dependencies = [
"windows-link 0.2.1",
"windows-result 0.4.1",
"windows-strings 0.5.1",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.3.4" version = "0.3.4"
@ -7029,10 +6689,7 @@ dependencies = [
"tauri", "tauri",
"tauri-build", "tauri-build",
"tauri-plugin-dialog", "tauri-plugin-dialog",
"tauri-plugin-fs",
"tauri-plugin-http",
"tauri-plugin-os", "tauri-plugin-os",
"tauri-plugin-persisted-scope",
"tauri-plugin-process", "tauri-plugin-process",
"tauri-plugin-single-instance", "tauri-plugin-single-instance",
"tauri-plugin-updater", "tauri-plugin-updater",

View File

@ -27,7 +27,7 @@ prod_150_8080 = []
[dependencies] [dependencies]
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = { version = "1.0.149", features = ["raw_value"] } serde_json = { version = "1.0.149", features = ["raw_value"] }
tauri-plugin-persisted-scope = "2.3.5" # tauri-plugin-persisted-scope = "2.3.5" # 已禁用,因为需要 fs 权限
log = "0.4.29" log = "0.4.29"
tauri = { version = "2.10.2", features = [ tauri = { version = "2.10.2", features = [
"macos-private-api", "macos-private-api",
@ -38,8 +38,6 @@ tauri = { version = "2.10.2", features = [
] } ] }
tauri-plugin-os = "2.3.2" tauri-plugin-os = "2.3.2"
tauri-plugin-dialog = "2.6.0" tauri-plugin-dialog = "2.6.0"
tauri-plugin-fs = "2.4.5"
tauri-plugin-http = "2.5.7"
tauri-plugin-process = "2.3.1" tauri-plugin-process = "2.3.1"
tauri-plugin-websocket = "2.4.2" tauri-plugin-websocket = "2.4.2"
rodio = "0.22.1" rodio = "0.22.1"
@ -51,20 +49,7 @@ tokio-cron-scheduler = "0.15.1"
tracing = "0.1.44" tracing = "0.1.44"
thiserror = "2.0.18" thiserror = "2.0.18"
bytes = "1.11.1" bytes = "1.11.1"
# Linux 平台依赖:获取工作区尺寸
# [target.'cfg(target_os = "linux")'.dependencies]
# x11-dl = "2.21"
# macOS 平台依赖
# [target.'cfg(target_os = "macos")'.dependencies]
# objc2 = "0.6"
# objc2-foundation = "0.3"
# objc2-app-kit = "0.3"
# Windows 平台依赖
# [target.'cfg(target_os = "windows")'.dependencies]
# windows = { version = "0.58", features = ["Win32_UI_WindowsAndMessaging", "Win32_Foundation"] }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-updater = "2.10.0" tauri-plugin-updater = "2.10.0"

View File

@ -3,25 +3,5 @@
"identifier": "desktop-capability", "identifier": "desktop-capability",
"platforms": ["macOS", "windows", "linux"], "platforms": ["macOS", "windows", "linux"],
"windows": ["main", "meeting", "meeting2", "popup", "float-list"], "windows": ["main", "meeting", "meeting2", "popup", "float-list"],
"permissions": [ "permissions": ["dialog:default", "updater:default", "process:default", "process:allow-exit", "websocket:default"]
"dialog:default",
"updater:default",
"process:default",
"process:allow-exit",
"fs:default",
"fs:allow-write-file",
"fs:allow-download-write",
"websocket:default",
{
"identifier": "http:default",
"allow": [
{
"url": "http://*:*"
},
{
"url": "https://*:*"
}
]
}
]
} }

View File

@ -31,14 +31,3 @@ pub const PLACEMENT_LEFT: &str = "left";
/// 右侧放置 /// 右侧放置
pub const PLACEMENT_RIGHT: &str = "right"; pub const PLACEMENT_RIGHT: &str = "right";
// ============ 存储键常量 ============
/// 用户信息存储键
pub const STORAGE_KEY_USER_INFO: &str = "tauri_store_page_user_info";
/// 辅导信息存储键
pub const STORAGE_KEY_TUTORSHIP: &str = "tauri_store_page_curr_tutorship";
/// 课程信息存储键
pub const STORAGE_KEY_COURSE: &str = "tauri_store_page_curr_course";

View File

@ -15,7 +15,6 @@
mod app; mod app;
mod audio; mod audio;
mod constants; mod constants;
mod env;
mod error; mod error;
mod invoke; mod invoke;
mod menu; mod menu;
@ -47,16 +46,10 @@ pub fn run() {
.plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_os::init())
// dialog 插件 - 显示文件选择对话框等 // dialog 插件 - 显示文件选择对话框等
.plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_dialog::init())
// fs 插件 - 文件系统访问
.plugin(tauri_plugin_fs::init())
// persisted_scope 插件 - 记住文件访问权限
.plugin(tauri_plugin_persisted_scope::init())
// process 插件 - 进程相关信息 // process 插件 - 进程相关信息
.plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_process::init())
// updater 插件 - 应用自动更新 // updater 插件 - 应用自动更新
.plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_updater::Builder::new().build())
// http 插件 - HTTP 客户端请求
.plugin(tauri_plugin_http::init())
// single_instance 插件 - 保证只启动一个应用实例 // single_instance 插件 - 保证只启动一个应用实例
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| { .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
let app_handle = app.clone(); let app_handle = app.clone();

View File

@ -1,4 +1 @@
pub mod session_storage; pub mod session_storage;
// 重新导出存储结构和命令
pub use session_storage::SessionStorage;

View File

@ -6,8 +6,8 @@ use tauri::{AppHandle, Window};
/// 创建悬浮列表窗口 /// 创建悬浮列表窗口
#[tauri::command] #[tauri::command]
pub async fn create_float_list_window(app: AppHandle, page_type: &str) -> Result<String, String> { pub async fn create_float_list_window(app: AppHandle) -> Result<String, String> {
create_float_list_window_impl(std::sync::Arc::new(app), page_type).await.map(|_| "success".to_string()) create_float_list_window_impl(std::sync::Arc::new(app)).await.map(|_| "success".to_string())
} }
/// 创建弹出框窗口 /// 创建弹出框窗口

View File

@ -1,25 +1,22 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
constants::{common::*, window::*}, constants::common::*,
storage::SessionStorage,
window::{ window::{
WindowLabel, WindowLabel,
derive::FloatListInfo, derive::FloatListInfo,
util::{get_monitor_for_window, show_windows_by_label}, util::{get_monitor_for_window, show_windows_by_label},
}, },
}; };
use tauri::{AppHandle, LogicalPosition, LogicalSize, Manager, Runtime, WebviewUrl, WebviewWindowBuilder, utils::config::BackgroundThrottlingPolicy}; use tauri::{AppHandle, LogicalPosition, LogicalSize, Runtime, WebviewUrl, WebviewWindowBuilder, utils::config::BackgroundThrottlingPolicy};
/// 创建悬浮列表窗口(内部实现) /// 创建悬浮列表窗口(内部实现)
pub async fn create_float_list_window_impl<R: Runtime>(app: Arc<AppHandle<R>>, page_type: &str) -> Result<(), String> { pub async fn create_float_list_window_impl<R: Runtime>(app: Arc<AppHandle<R>>) -> Result<(), String> {
let app_handle = Arc::clone(&app);
let page_type_owned = page_type.to_string();
if show_windows_by_label(app.as_ref(), WindowLabel::FLOAT_LIST) { if show_windows_by_label(app.as_ref(), WindowLabel::FLOAT_LIST) {
return Ok(()); return Ok(());
} }
// 悬浮列表尺寸 // 悬浮列表尺寸
let float_info = calculate_float_info_sync(app.as_ref(), page_type); let float_info = calculate_float_info_sync();
// 获取屏幕尺寸优先为main窗口所在屏幕主窗口没有打开时才找主屏幕 // 获取屏幕尺寸优先为main窗口所在屏幕主窗口没有打开时才找主屏幕
let monitor = get_monitor_for_window(app.as_ref())?; let monitor = get_monitor_for_window(app.as_ref())?;
// 获取屏幕缩放因子 // 获取屏幕缩放因子
@ -27,10 +24,7 @@ pub async fn create_float_list_window_impl<R: Runtime>(app: Arc<AppHandle<R>>, p
let page_size: LogicalSize<f64> = monitor.work_area().size.to_logical(scale_factor); let page_size: LogicalSize<f64> = monitor.work_area().size.to_logical(scale_factor);
let screen_width = page_size.width; let screen_width = page_size.width;
let screen_height = page_size.height; let screen_height = page_size.height;
let url = format!( let url = format!("floating-list-window.html");
"floating-list-window.html?isShowInteraction={}&isShowTutorship={}",
float_info.is_show_interaction, float_info.is_show_tutorship
);
let label = WindowLabel::FLOAT_LIST.as_ref(); let label = WindowLabel::FLOAT_LIST.as_ref();
// 创建悬浮球窗口,直接在初始化时设置好位置和尺寸 // 创建悬浮球窗口,直接在初始化时设置好位置和尺寸
@ -73,78 +67,15 @@ pub async fn create_float_list_window_impl<R: Runtime>(app: Arc<AppHandle<R>>, p
// 窗口显示后等待200毫秒再执行动画 // 窗口显示后等待200毫秒再执行动画
tokio::spawn(async move { tokio::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
let _ = animate_to_bottom_right(&app_handle, window, screen_width as i32, screen_height as i32, &page_type_owned).await; let _ = animate_to_bottom_right(window, screen_width as i32, screen_height as i32).await;
}); });
Ok(()) Ok(())
} }
/// 根据用户登录状态和课程选择情况动态计算弹出窗口高度 /// 根据用户登录状态和课程选择情况动态计算弹出窗口高度
pub fn calculate_float_info_sync<R: Runtime>(app: &AppHandle<R>, page_type: &str) -> FloatListInfo { pub fn calculate_float_info_sync() -> FloatListInfo {
// 默认基础项目数 // 默认基础项目数
let mut item_num = 4; let item_num = 4;
let mut is_show_interaction = false;
let mut is_show_tutorship = false;
// 获取会话存储实例
let storage = match app.try_state::<SessionStorage>() {
Some(s) => s,
None => {
log::error!("获取会话存储实例失败,使用默认值");
let item_height = 64;
let popup_height = item_height * item_num + 40;
let popup_width = 60;
return FloatListInfo {
width: popup_width as u32,
height: popup_height as u32,
is_show_interaction,
is_show_tutorship,
};
}
};
// 从会话存储中获取用户信息和课程信息来判断是否增加项目数
let user_info_exists = match storage.get::<serde_json::Value>(STORAGE_KEY_USER_INFO) {
Some(Ok(value)) => {
// 检查必要的字段是否存在
value.get("id").is_some()
}
_ => false,
};
let tutorship_info_exists = match storage.get::<serde_json::Value>(STORAGE_KEY_TUTORSHIP) {
Some(Ok(value)) => {
// 检查必要的字段是否存在
value.get("id").is_some()
}
_ => false,
};
let course_info_exists = match storage.get::<serde_json::Value>(STORAGE_KEY_COURSE) {
Some(Ok(value)) => {
// 检查必要的字段是否存在
value.get("id").is_some()
}
_ => false,
};
if user_info_exists {
if page_type == "interaction" {
is_show_interaction = true;
// 如果能获取到用户信息和课程信息,则增加项目数
if course_info_exists {
item_num += 5;
}
} else if page_type == "tutorship" {
// 如果能获取到用户信息和课程信息,则增加项目数
if course_info_exists {
is_show_interaction = true;
item_num += 5;
}
if tutorship_info_exists {
is_show_tutorship = true;
item_num += 3;
}
}
}
// 每个item高度 // 每个item高度
let item_height = 64; let item_height = 64;
// 总高度 = item高度 * 项目数 + 额外用于移动按钮和时间部分的高度 // 总高度 = item高度 * 项目数 + 额外用于移动按钮和时间部分的高度
@ -155,19 +86,11 @@ pub fn calculate_float_info_sync<R: Runtime>(app: &AppHandle<R>, page_type: &str
FloatListInfo { FloatListInfo {
width: popup_width as u32, width: popup_width as u32,
height: popup_height as u32, height: popup_height as u32,
is_show_tutorship,
is_show_interaction,
} }
} }
/// 使用抛物线动画将窗口移动到右下角 /// 使用抛物线动画将窗口移动到右下角
pub async fn animate_to_bottom_right<R: Runtime>( pub async fn animate_to_bottom_right<R: Runtime>(window: tauri::WebviewWindow<R>, screen_width: i32, screen_height: i32) -> Result<(), String> {
app: &AppHandle<R>,
window: tauri::WebviewWindow<R>,
screen_width: i32,
screen_height: i32,
page_type: &str,
) -> Result<(), String> {
// 获取当前窗口位置和大小 // 获取当前窗口位置和大小
let current_position_physical = window.outer_position().map_err(|e| format!("获取窗口位置失败: {}", e))?; let current_position_physical = window.outer_position().map_err(|e| format!("获取窗口位置失败: {}", e))?;
let current_size_physical = window.outer_size().map_err(|e| format!("获取窗口大小失败: {}", e))?; let current_size_physical = window.outer_size().map_err(|e| format!("获取窗口大小失败: {}", e))?;
@ -225,7 +148,7 @@ pub async fn animate_to_bottom_right<R: Runtime>(
} }
// 确保最终位置准确, 再次获取窗口大小和缩放因子 // 确保最终位置准确, 再次获取窗口大小和缩放因子
let float_info = calculate_float_info_sync(app, page_type); let float_info = calculate_float_info_sync();
let current_size_physical = window.outer_size().map_err(|e| format!("获取窗口大小失败: {}", e))?; let current_size_physical = window.outer_size().map_err(|e| format!("获取窗口大小失败: {}", e))?;
let scale_factor = window.scale_factor().map_err(|e| format!("获取缩放因子失败: {}", e))?; let scale_factor = window.scale_factor().map_err(|e| format!("获取缩放因子失败: {}", e))?;
let current_size = current_size_physical.to_logical::<i32>(scale_factor); let current_size = current_size_physical.to_logical::<i32>(scale_factor);

View File

@ -1,5 +1,7 @@
use crate::constants::{common::APP_NAME, window::*}; use crate::constants::{common::APP_NAME, window::*};
use crate::window::{util::show_windows_by_label, WindowLabel}; use crate::window::{WindowLabel, util::show_windows_by_label};
use tauri::utils::config::BackgroundThrottlingPolicy;
use tauri::window::Color;
use tauri::{AppHandle, Runtime, WebviewUrl, WebviewWindowBuilder}; use tauri::{AppHandle, Runtime, WebviewUrl, WebviewWindowBuilder};
/// 根据tauri.conf.json配置创建主窗口 /// 根据tauri.conf.json配置创建主窗口
@ -17,7 +19,9 @@ pub async fn create_main_window_impl<R: Runtime>(app: &AppHandle<R>) -> Result<(
.resizable(true) .resizable(true)
.fullscreen(false) .fullscreen(false)
.decorations(false) .decorations(false)
.transparent(true) .transparent(false)
.background_throttling(BackgroundThrottlingPolicy::Disabled)
.background_color(Color(255, 255, 255, 255))
.always_on_top(false) .always_on_top(false)
.center() .center()
.shadow(true) .shadow(true)

View File

@ -29,10 +29,6 @@ pub struct FloatListInfo {
pub width: u32, pub width: u32,
/// 弹出框的高度 /// 弹出框的高度
pub height: u32, pub height: u32,
/** 是否显示互动课堂相关按钮 */
pub is_show_interaction: bool,
/** 是否显示智评会议相关按钮 */
pub is_show_tutorship: bool,
} }
/// 弹出框位置信息 /// 弹出框位置信息

View File

@ -8,166 +8,39 @@
> >
<div style="width: 100%; height: 6px; background: rgba(255, 255, 255, 0.6); border-radius: 4px"></div> <div style="width: 100%; height: 6px; background: rgba(255, 255, 255, 0.6); border-radius: 4px"></div>
</div> </div>
<div class="time-container">
<div class="time-box">
<span class="time-text">{{ timeData.hour }}:{{ timeData.minute }}</span>
</div>
</div>
<template v-if="isShowInteraction">
<div class="menu-item" @click="openChoiceTopic()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_01_01.png" />
<span class="menu-text">选择题</span>
</div>
<div class="menu-item" @click="openRealTimeTopic()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_01_02.png" />
<span class="menu-text">实时题</span>
</div>
<div class="menu-item" @click="openCompetitionTopic()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_01_03.png" />
<span class="menu-text">竞赛题</span>
</div>
<div class="menu-item" @click="openJudgeTopic()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_01_04.png" />
<span class="menu-text">判断题</span>
</div>
<div class="menu-item" @click="openCloudDisk()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_01_05.png" />
<span class="menu-text">云盘</span>
</div>
<!--
<div class="menu-item" @click="openPk()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_01_06.png" />
<span class="menu-text">PK赛</span>
</div>
-->
</template>
<template v-if="isShowTutorship">
<div style="width: calc(100% - 12px); height: 1px; margin: 9px 10px; background: rgba(180, 180, 180, 0.6)"></div>
<div class="menu-item tutorship-item" @click="openTutorship()">
<img class="menu-icon" style="width: 32px; height: 32px; margin-bottom: 2px" src="@/assets/images/floating-list-window/icon_02_01.png" />
<span class="menu-text">评讲</span>
</div>
<div class="menu-item tutorship-item" @click="openMeeting()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_02_02.png" />
<span class="menu-text">会议控制</span>
</div>
<div class="menu-item tutorship-item" @click="openSubtitle()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_02_03.png" />
<span class="menu-text">实时字幕</span>
</div>
</template>
<div style="width: calc(100% - 12px); height: 1px; margin: 9px 10px; background: rgba(180, 180, 180, 0.6)"></div> <div style="width: calc(100% - 12px); height: 1px; margin: 9px 10px; background: rgba(180, 180, 180, 0.6)"></div>
<div class="menu-item" @click="openDrawingBoard()"> <!-- 会议控制按钮 -->
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_03_01.png" /> <div class="menu-item" @click="muteAllStudents()">
<span class="menu-text">画板</span> <img class="menu-icon" src="@/assets/images/floating-list-window/icon_02_04.png" />
<span class="menu-text">全员静音</span>
</div> </div>
<div class="menu-item" @click="switchCourses()"> <div class="menu-item" @click="muteTeacher()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_03_03.png" /> <img class="menu-icon" src="@/assets/images/floating-list-window/icon_02_05.png" />
<span class="menu-text">切换课程</span> <span class="menu-text">老师静音</span>
</div> </div>
<div class="menu-item" @click="logout()"> <div class="menu-item" @click="endClass()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_03_04.png" /> <img class="menu-icon" src="@/assets/images/floating-list-window/icon_02_06.png" />
<span class="menu-text">退出登录</span> <span class="menu-text">下课</span>
</div>
<div class="menu-item" @click="openConsole()">
<img class="menu-icon" src="@/assets/images/floating-list-window/icon_02_07.png" />
<span class="menu-text">控制台</span>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue';
import { emitTo } from '@tauri-apps/api/event'; import { emitTo } from '@tauri-apps/api/event';
import { createDrawingBoardWindow, createMainWindow, createMeetingWindow, createSubtitleWindow } from '@/utils/tauri/windows-action';
import { windowLabel } from '@/utils/tauri/windows-util'; import { windowLabel } from '@/utils/tauri/windows-util';
import { tauri_floating_list_page_action } from '@/utils/constant'; import { tauri_floating_list_page_action } from '@/utils/constant';
import { handleMouseDown, handleTouchStart, startWindowX, startWindowY } from '../move-window'; import { handleMouseDown, handleTouchStart, startWindowX, startWindowY } from '../move-window';
import { LogicalSize, getCurrentWindow } from '@tauri-apps/api/window'; import { getCurrentWindow } from '@tauri-apps/api/window';
import { useTauriSessionStorage } from './use-tauri-session-storage';
import { playPromptTone } from '@/utils/prompt-tone'; import { playPromptTone } from '@/utils/prompt-tone';
import type { FloatingListPageAction } from '@/utils/tauri/types'; import type { FloatingListPageAction } from '@/utils/tauri/types';
const isFold = defineModel<boolean>(); const isFold = defineModel<boolean>();
const timeData = ref({ hour: '00', minute: '00' });
let timer: NodeJS.Timeout | null = null;
const currentWindow = getCurrentWindow(); const currentWindow = getCurrentWindow();
const isShowInteraction = ref(false);
const isShowTutorship = ref(false);
const { userInfo, currTutorship, currCourse, updateBaseDate } = useTauriSessionStorage();
watch(
() => [currTutorship.value, currCourse.value, userInfo.value],
([_currTutorship, _currCourse, _userInfo]) => {
let item_num = 4;
if (_userInfo) {
if (currCourse.value) {
isShowInteraction.value = true;
item_num += 5;
} else {
isShowInteraction.value = false;
}
if (_currTutorship) {
isShowTutorship.value = true;
item_num += 3;
} else {
isShowTutorship.value = false;
}
} else {
isShowInteraction.value = false;
isShowTutorship.value = false;
}
// 总高度 = item高度 * 项目数 + 额外用于移动按钮和时间部分的高度
const popup_height = 64 * item_num + 60;
const popup_width = 60;
currentWindow.setSize(new LogicalSize(popup_width, popup_height));
}
);
onMounted(() => {
setInterval(() => {
updateBaseDate();
}, 2000);
updateTime(); // 初始化时立即更新
scheduleNextUpdate(); // 设置后续更新
updateBaseDate();
});
onBeforeUnmount(() => {
timer && clearTimeout(timer);
});
/**
* 计算到下一秒的毫秒数
*/
function getDelayToNextSecond(): number {
const now = new Date();
const delay = 1000 - now.getMilliseconds();
return delay;
}
/**
* 设置下一个更新周期
*/
function scheduleNextUpdate() {
const delay = getDelayToNextSecond();
timer = setTimeout(() => {
updateTime();
scheduleNextUpdate(); // 递归调用以继续更新
}, delay);
}
/**
* 更新当前时间数据和格式化字符串
*/
function updateTime() {
const now = new Date();
timeData.value = {
hour: now.getHours().toString().padStart(2, '0'),
minute: now.getMinutes().toString().padStart(2, '0'),
};
}
/** /**
* 切换折叠状态 * 切换折叠状态
@ -185,110 +58,45 @@
isFold.value = !isFold.value; isFold.value = !isFold.value;
} }
} }
/**
* 全员静音 (不包括老师)
*/
function muteAllStudents() {
playPromptTone();
// 发送事件给主窗口,让主窗口的 socket 处理
emitTo(windowLabel.main, 'meeting-control', {
action: 'mute_all_students',
});
}
/** /**
* 打开选择题 * 老师静音
*/ */
async function openChoiceTopic() { function muteTeacher() {
playPromptTone();
// 发送事件给主窗口,让主窗口的 socket 处理
emitTo(windowLabel.main, 'meeting-control', {
action: 'mute_teacher',
});
}
/**
* 下课
*/
function endClass() {
playPromptTone();
// 发送事件给主窗口,让主窗口的 socket 处理
emitTo(windowLabel.main, 'meeting-control', {
action: 'end_class',
});
}
/**
* 打开控制台 (显示主窗口)
*/
async function openConsole() {
playPromptTone(); playPromptTone();
await createMainWindow(); await createMainWindow();
sendtPageActionEmit('open-choice-topic');
}
/**
* 实时主观题
*/
async function openRealTimeTopic() {
playPromptTone();
await createMainWindow();
sendtPageActionEmit('open-real-time-topic');
}
/**
* 竞赛主观题
*/
async function openCompetitionTopic() {
playPromptTone();
await createMainWindow();
sendtPageActionEmit('open-competition-topic');
}
/**
* 判断题
*/
async function openJudgeTopic() {
playPromptTone();
await createMainWindow();
sendtPageActionEmit('open-judge-topic');
}
/**
* 打开辅导课程
*/
async function openTutorship() {
playPromptTone();
await createMainWindow();
sendtPageActionEmit('open-tutorship');
}
/**
* 打开云盘
*/
async function openCloudDisk() {
playPromptTone();
await createMainWindow();
sendtPageActionEmit('open-cloud-disk');
}
/**
* 打开PK赛
*/
// async function openPk() {
// playPromptTone();
// await createMainWindow();
// sendtPageActionEmit('open-pk');
// }
/**
* 切换课程
*/
async function switchCourses() {
playPromptTone();
await createMainWindow();
sendtPageActionEmit('switch-courses');
}
/**
* 打开会议
*/
async function openMeeting() {
playPromptTone();
await createMeetingWindow(2, currTutorship.value?.id);
}
/**
* 打开字幕
*/
async function openSubtitle() {
playPromptTone();
await createSubtitleWindow();
}
/**
* 打开画板
*/
async function openDrawingBoard() {
playPromptTone();
await createDrawingBoardWindow();
}
/**
* 退出登录
*/
async function logout() {
playPromptTone();
await createMainWindow();
sendtPageActionEmit('logout');
} }
/** /**

View File

@ -1,109 +0,0 @@
import { isObject } from '@/utils/verify';
import { STORAGE_COMMANDS, tauriSessionStorage } from '@/utils/tauri/tauri-session';
import type { BriUserInfo } from '@/api/login';
import { tauri_store_page_curr_course, tauri_store_page_curr_tutorship, tauri_store_page_user_info } from '@/utils/constant';
import { getCurrentWindow } from '@tauri-apps/api/window';
import { ref } from 'vue';
/**
* 课程信息(悬浮列表窗口只关心必要字段)
*/
export type BriCourse = {
/** 课程 ID */
id: number;
/** 班级 ID */
classId: number;
/** 其他字段 */
[key: string]: unknown;
};
/**
* 评讲信息(悬浮列表窗口只关心必要字段)
*/
export type BriTutorship = {
/** 评讲 ID */
id: number;
/** 其他字段 */
[key: string]: unknown;
};
/**
* useTauriSessionStorage
*/
export function useTauriSessionStorage() {
const userInfo = ref<BriUserInfo | null>(null);
const currTutorship = ref<BriTutorship | null>(null);
const currCourse = ref<BriCourse | null>(null);
const currentWindow = getCurrentWindow();
updateBaseDate();
// 监听后端存储数据变更
currentWindow.listen<[string, string]>(STORAGE_COMMANDS.CHANGE, ({ payload: [key, value] }) => {
if (key === tauri_store_page_curr_tutorship) {
const _currTutorship = isObject(value) ? value : null;
if (_currTutorship && 'id' in _currTutorship) {
currTutorship.value = _currTutorship as unknown as BriTutorship;
}
} else if (key === tauri_store_page_curr_course) {
const _currCourse = isObject(value) ? value : null;
if (_currCourse && 'id' in _currCourse && 'classId' in _currCourse) {
currCourse.value = _currCourse as unknown as BriCourse;
}
} else if (key === tauri_store_page_user_info) {
const _userInfo = isObject(value) ? value : null;
if (_userInfo && 'id' in _userInfo && 'schoolId' in _userInfo) {
userInfo.value = _userInfo as unknown as BriUserInfo;
}
}
});
// 监听后端数据删除事件
currentWindow.listen<string>(STORAGE_COMMANDS.REMOVE, (event) => {
const key = event.payload;
if (key === tauri_store_page_curr_tutorship) {
currTutorship.value = null;
} else if (key === tauri_store_page_curr_course) {
currCourse.value = null;
} else if (key === tauri_store_page_user_info) {
userInfo.value = null;
}
});
// 监听后端清空事件
currentWindow.listen(STORAGE_COMMANDS.CLEAR, () => {
currTutorship.value = null;
currCourse.value = null;
userInfo.value = null;
});
/**
* 跟新基础数据
*/
function updateBaseDate() {
tauriSessionStorage.get(tauri_store_page_user_info).then((value) => {
const obj = isObject(value) ? value : null;
if (obj && 'id' in obj && 'schoolId' in obj) {
userInfo.value = obj as unknown as BriUserInfo;
}
});
tauriSessionStorage.get(tauri_store_page_curr_tutorship).then((value) => {
const obj = isObject(value) ? value : null;
if (obj && 'id' in obj) {
currTutorship.value = obj as unknown as BriTutorship;
}
});
tauriSessionStorage.get(tauri_store_page_curr_course).then((value) => {
const obj = isObject(value) ? value : null;
if (obj && 'id' in obj && 'classId' in obj) {
currCourse.value = obj as unknown as BriCourse;
}
});
}
return {
/** 更新基础数据 */
updateBaseDate,
userInfo,
currTutorship,
currCourse,
};
}

View File

@ -177,11 +177,13 @@
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useStreamType } from './useStreamType'; import { useStreamType } from './useStreamType';
import { usePermissions } from './usePermissions'; import { usePermissions } from './usePermissions';
import { listen } from '@tauri-apps/api/event';
import type { DisplayUser, OnJoinRoomData, ServerToClientEvents } from './types'; import type { DisplayUser, OnJoinRoomData, ServerToClientEvents } from './types';
import { useAgoraMeeting } from './useAgoraMeeting'; import { useAgoraMeeting } from './useAgoraMeeting';
import { destroyMeetingSocket, useMeetingSocket, useWebSocket } from './websocket'; import { destroyMeetingSocket, useMeetingSocket, useWebSocket } from './websocket';
import { getBlacklistAxios, getStudentDetailsAxios, removeFromBlacklistAxios } from '@/api/meeting'; import { getBlacklistAxios, getStudentDetailsAxios, removeFromBlacklistAxios } from '@/api/meeting';
import StudentSortDialog from './student-sort-dialog.vue'; import StudentSortDialog from './student-sort-dialog.vue';
import { createFloatListWindow } from '@/utils/tauri/windows-action';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -528,7 +530,7 @@
} }
case 'sev_class_started': { case 'sev_class_started': {
classStatus.value = 'in_class'; classStatus.value = 'in_class';
if (meeting.client.value) { if (meeting.client.value) {
fetchStudentList(); fetchStudentList();
} else { } else {
@ -617,7 +619,7 @@
if (localUid && localUid !== 0) { if (localUid && localUid !== 0) {
result.push(meeting.localUser as DisplayUser); result.push(meeting.localUser as DisplayUser);
} }
if (selectedUserUid.value) { if (selectedUserUid.value) {
const idx = result.findIndex((u) => u.uid === selectedUserUid.value); const idx = result.findIndex((u) => u.uid === selectedUserUid.value);
if (idx > 0) { if (idx > 0) {
@ -687,7 +689,7 @@
const result = await getStudentDetailsAxios(homeworkId.value, roomId.value); const result = await getStudentDetailsAxios(homeworkId.value, roomId.value);
if (result.data && Array.isArray(result.data)) { if (result.data && Array.isArray(result.data)) {
studentListForSort.value = result.data; studentListForSort.value = result.data;
const longIdMap = new Map<number, string>(); const longIdMap = new Map<number, string>();
result.data.forEach((student) => { result.data.forEach((student) => {
longIdMap.set(student.longId, student.studentName); longIdMap.set(student.longId, student.studentName);
@ -725,6 +727,7 @@
// 下课:发送下课消息并离开 // 下课:发送下课消息并离开
endClassAndLeave(); endClassAndLeave();
} else { } else {
createFloatListWindow('interaction');
// 上课:发送上课消息 // 上课:发送上课消息
socket.emit('client_start_class', { roomId: roomId.value, meetingDuration: meetingDuration.value }); socket.emit('client_start_class', { roomId: roomId.value, meetingDuration: meetingDuration.value });
} }
@ -893,6 +896,81 @@
} }
} }
/**
* 处理来自悬浮窗的会议控制事件
* @param action - 控制动作
*/
async function handleMeetingControl(action: string): Promise<void> {
console.log('[教师端] 处理会议控制动作:', action);
switch (action) {
case 'mute_all_students': {
// 全员静音 (不包括老师)
await muteAllStudents();
break;
}
case 'mute_teacher': {
// 老师静音
await muteTeacher();
break;
}
case 'end_class': {
// 下课
await endClassAndLeave();
break;
}
default:
console.warn('[教师端] 未知的控制动作:', action);
}
}
/**
* 全员静音 (不包括老师)
*/
async function muteAllStudents(): Promise<void> {
if (!roomId.value) {
Toast.warning('房间未初始化');
return;
}
try {
// 遍历所有学生用户,发送禁麦消息
const studentUids = studentUsers.value.map((u) => u.uid);
for (const uid of studentUids) {
socket.emit('client_mute_audio', {
roomId: roomId.value,
targetUid: uid,
});
}
Toast.success('已全员静音');
} catch (error) {
console.error('全员静音失败:', error);
Toast.error('全员静音失败');
}
}
/**
* 老师静音
*/
async function muteTeacher(): Promise<void> {
if (!roomId.value || !shortUid.value) {
Toast.warning('房间未初始化');
return;
}
try {
// 给自己发送禁麦消息
socket.emit('client_mute_audio', {
roomId: roomId.value,
targetUid: shortUid.value,
});
Toast.success('老师已静音');
} catch (error) {
console.error('老师静音失败:', error);
Toast.error('老师静音失败');
}
}
/** /**
* 组件挂载生命周期钩子 * 组件挂载生命周期钩子
*/ */
@ -917,6 +995,12 @@
} else { } else {
Toast.info?.('等待老师上课...'); Toast.info?.('等待老师上课...');
} }
// 监听来自悬浮窗的控制事件
listen<{ action: string }>('meeting-control', (event) => {
console.log('[教师端] 收到会议控制事件:', event.payload);
handleMeetingControl(event.payload.action);
});
} catch (error) { } catch (error) {
console.error('加入会议失败:', error); console.error('加入会议失败:', error);
const errorMessage = error instanceof Error ? error.message : '加入会议失败'; const errorMessage = error instanceof Error ? error.message : '加入会议失败';