Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
fc-minigame
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
陈冲
fc-minigame
Commits
40a1a7b0
Commit
40a1a7b0
authored
Jun 09, 2026
by
陈冲
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 功能修复
parent
a8efe97a
Show whitespace changes
Inline
Side-by-side
Showing
31 changed files
with
937 additions
and
192 deletions
+937
-192
Cargo.lock
backend/Cargo.lock
+51
-4
Cargo.toml
backend/Cargo.toml
+1
-0
config.rs
backend/src/commons/config.rs
+4
-2
utils.rs
backend/src/commons/utils.rs
+53
-0
mod.rs
backend/src/db/mod.rs
+5
-1
game.rs
backend/src/db/models/game.rs
+5
-2
manager.rs
backend/src/db/models/manager.rs
+11
-2
mysql.rs
backend/src/db/mysql.rs
+1
-1
pg.rs
backend/src/db/pg.rs
+1
-1
socket_io.rs
backend/src/protocols/socket_io.rs
+127
-49
utils.ts
frontend-h5/src/commons/utils.ts
+263
-0
ws.ts
frontend-h5/src/commons/ws.ts
+15
-15
useGameSocket.ts
frontend-h5/src/composables/useGameSocket.ts
+18
-15
Game.vue
frontend-h5/src/pages/game1/Game.vue
+6
-4
LoadingView.vue
frontend-h5/src/pages/game1/views/LoadingView.vue
+6
-5
Game4.vue
frontend-h5/src/pages/game4/Game4.vue
+6
-4
Game5.vue
frontend-h5/src/pages/game5/Game5.vue
+6
-4
LoadingView.vue
frontend-h5/src/pages/game5/views/LoadingView.vue
+11
-9
Game.vue
frontend-h5/src/pages/game6/Game.vue
+5
-4
LoadingView.vue
frontend-h5/src/pages/game6/views/LoadingView.vue
+6
-6
podium.png
frontend-large/src/assets/images/game1/podium.png
+0
-0
music.ts
frontend-large/src/commons/music.ts
+21
-11
utils.ts
frontend-large/src/commons/utils.ts
+230
-0
ws.ts
frontend-large/src/commons/ws.ts
+2
-2
useAdminGameSocket.ts
frontend-large/src/composables/useAdminGameSocket.ts
+5
-8
Login.vue
frontend-large/src/pages/Login.vue
+9
-7
Game.vue
frontend-large/src/pages/game1/Game.vue
+14
-7
Rank2View.vue
frontend-large/src/pages/game1/views/Rank2View.vue
+19
-14
Game4.vue
frontend-large/src/pages/game4/Game4.vue
+12
-5
Game5.vue
frontend-large/src/pages/game5/Game5.vue
+12
-5
Game.vue
frontend-large/src/pages/game6/Game.vue
+12
-5
No files found.
backend/Cargo.lock
View file @
40a1a7b0
...
...
@@ -379,6 +379,16 @@ dependencies = [
[[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]]
name = "core-foundation"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
...
...
@@ -1044,6 +1054,7 @@ dependencies = [
"lazy_static",
"md-5",
"rand 0.10.0",
"reqwest",
"rmp-serde",
"rust-embed",
"salvo",
...
...
@@ -1222,9 +1233,11 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"socket2",
"system-configuration",
"tokio",
"tower-service",
"tracing",
"windows-registry",
]
[[package]]
...
...
@@ -2187,9 +2200,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "reqwest"
version = "0.13.
2
"
version = "0.13.
4
"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "
ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801
"
checksum = "
219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3
"
dependencies = [
"base64",
"bytes",
...
...
@@ -2212,6 +2225,8 @@ dependencies = [
"rustls",
"rustls-pki-types",
"rustls-platform-verifier",
"serde",
"serde_json",
"sync_wrapper",
"tokio",
"tokio-rustls",
...
...
@@ -2376,7 +2391,7 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
dependencies = [
"core-foundation",
"core-foundation
0.10.1
",
"core-foundation-sys",
"jni",
"log",
...
...
@@ -2607,7 +2622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
"bitflags",
"core-foundation",
"core-foundation
0.10.1
",
"core-foundation-sys",
"libc",
"security-framework-sys",
...
...
@@ -3133,6 +3148,27 @@ dependencies = [
]
[[package]]
name = "system-configuration"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
dependencies = [
"bitflags",
"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]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
...
...
@@ -3874,6 +3910,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
dependencies = [
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
...
...
backend/Cargo.toml
View file @
40a1a7b0
...
...
@@ -25,6 +25,7 @@ lazy_static = "1.5.0"
aes
=
"0.8.4"
cbc
=
{
version
=
"0.1.2"
,
features
=
["alloc"]
}
md5
=
{
version
=
"0.10.6"
,
package
=
"md-5"
}
reqwest
=
{
version
=
"0.13.4"
,
features
=
["json"]
}
[[bin]]
name
=
"hddpServer"
...
...
backend/src/commons/config.rs
View file @
40a1a7b0
...
...
@@ -15,7 +15,8 @@ pub struct Config {
#[serde(skip_serializing_if
=
"Option::is_none"
)]
pub
aes_passphrase
:
Option
<
String
>
,
// 数据库配置:所有 master/worker 节点都可以直连同一台 DB。
pub
db
:
DbConfig
,
pub
db
:
Option
<
DbConfig
>
,
// pub post: String,
}
#[derive(Clone,
Deserialize,
Serialize,
Debug)]
...
...
@@ -34,7 +35,8 @@ impl Default for Config {
client_ns
:
"/ws"
.to_string
(),
socket_path
:
"/socket.io"
.to_string
(),
aes_passphrase
:
None
,
db
:
DbConfig
::
default
(),
db
:
None
,
//DbConfig::default(),
// post: "".to_string(),
}
}
}
...
...
backend/src/commons/utils.rs
View file @
40a1a7b0
...
...
@@ -7,6 +7,10 @@ use base64::{Engine as _, engine::general_purpose};
use
cbc
::
Decryptor
;
use
md5
::{
Digest
,
Md5
};
use
rand
::
Rng
;
use
reqwest
::
Client
;
use
std
::
option
::
Option
;
use
serde
::
Serialize
;
use
std
::
collections
::
HashMap
;
use
std
::
sync
::
LazyLock
;
pub
static
CONFIG
:
LazyLock
<
Config
>
=
LazyLock
::
new
(||
Config
::
load
()
.unwrap
());
...
...
@@ -141,3 +145,52 @@ pub fn add_minute(ts: u64, val: u64) -> u64 {
pub
fn
add_hour
(
ts
:
u64
,
val
:
u64
)
->
u64
{
add_minute
(
ts
,
val
*
60
)
}
// ///返回为空时表示提交异常
// pub async fn post(token: &str, index: i64) -> Result<Option<String>, Box<dyn std::error::Error>> {
// let url = CONFIG.post.clone();
// #[derive(Debug, Serialize)]
// struct RequestBody {
// activity_id: i32,
// game_code: String,
// page: i32,
// pageSize: i32,
// }
// let mut map = HashMap::new();
// map.insert(1, "JGQF");
// map.insert(2, "JBJF");
// map.insert(3, "MSYF");
// map.insert(4, "CMYF");
// map.insert(5, "QCNF");
// map.insert(6, "FYDT");
// let code = map.get(&index).and_then(|v| Some("")).unwrap();
// let body = RequestBody {
// activity_id: 1,
// game_code: code.to_string(), //大屏游戏代码 (马上有福:MSYF, 击鼓齐福:JGQF, 策马迎福:CMYF, 圈彩纳福:QCNF, 福运当头:FYDT, 聚宝接福:JBJF)
// page: 1,
// pageSize: 20,
// };
// let client = Client::new();
// let resp = client
// .post(url)
// .bearer_auth(token)
// .json(&body)
// .send()
// .await?;
// let status = resp.status();
// if !status.is_success() {
// let text = resp.text().await?;
// println!("请求失败: {}, body: {}", status, text);
// return Ok(None);
// }
// let result = resp.text().await?;
// println!("响应: {:?}", result);
// Ok(Some(result))
// }
backend/src/db/mod.rs
View file @
40a1a7b0
...
...
@@ -18,7 +18,8 @@ trait DbDriver: Send + Sync {
static
DB_CONN
:
OnceCell
<
Box
<
dyn
DbDriver
>>
=
OnceCell
::
const_new
();
pub
async
fn
init_db
()
->
Result
<
(),
String
>
{
let
conn
:
Box
<
dyn
DbDriver
>
=
match
config
.db.kind
.as_str
()
{
if
let
Some
(
db
)
=
&
config
.db
{
let
conn
:
Box
<
dyn
DbDriver
>
=
match
db
.kind
.as_str
()
{
"postgresql"
=>
Box
::
new
(
PgDb
::
new
()
.await
?
),
"mysql"
=>
Box
::
new
(
MysqlDb
::
new
()
.await
?
),
other
=>
{
...
...
@@ -29,6 +30,9 @@ pub async fn init_db() -> Result<(), String> {
DB_CONN
.set
(
conn
)
.map_err
(|
_
|
"db already initialized"
.to_string
())
}
else
{
Ok
(())
}
}
pub
async
fn
db_query
(
sql
:
&
str
,
params
:
Vec
<
Value
>
)
->
Result
<
Vec
<
Value
>
,
String
>
{
...
...
backend/src/db/models/game.rs
View file @
40a1a7b0
use
crate
::{
commons
::
utils
::
CONFIG
as
config
,
db
::
models
::
log
::
Log
};
use
chrono
::{
DateTime
,
NaiveDate
,
Utc
};
use
serde_json
::
json
;
use
crate
::
db
::
models
::
log
::
Log
;
pub
struct
Game
{
pub
wechat_id
:
String
,
pub
nickname
:
String
,
...
...
@@ -19,6 +18,10 @@ pub struct Game {
impl
Game
{
pub
async
fn
insert
(
&
self
)
->
bool
{
let
Some
(
_
)
=
&
config
.db
else
{
println!
(
"没有配置数据库, return true;"
);
return
true
;
};
let
res
=
crate
::
db
::
db_query
(
r
#
"insert into tb_game(wechat_id,nickname,avatar,item_num,game_rank,score,create_date) values(?,?,?,?,?,?,now());"
#
,
vec!
[
...
...
backend/src/db/models/manager.rs
View file @
40a1a7b0
use
serde_json
::{
Value
,
json
};
use
crate
::
commons
::
utils
::
CONFIG
as
config
;
use
crate
::
db
::
models
::
*
;
use
serde_json
::{
Value
,
json
};
#[derive(Debug)]
pub
struct
Manager
{
...
...
@@ -18,6 +18,15 @@ pub enum ManagerLogin {
impl
Manager
{
pub
async
fn
Login
(
m
:
ManagerLogin
)
->
Result
<
Self
,
String
>
{
let
Some
(
_
)
=
&
config
.db
else
{
println!
(
"没有配置数据库, return Self;"
);
return
Ok
(
Manager
{
id
:
1
,
username
:
"admin"
.to_string
(),
password
:
"123456"
.to_string
(),
token
:
"88236aeb-8d3f-6d56-fdf5-3adca3961df1"
.to_string
(),
});
};
match
m
{
ManagerLogin
::
user
(
username
,
password
)
=>
{
let
rows
=
crate
::
db
::
db_query
(
...
...
backend/src/db/mysql.rs
View file @
40a1a7b0
...
...
@@ -21,7 +21,7 @@ impl MysqlDb {
})
})
.max_connections
(
16
)
.connect
(
&
config
.db.uri
)
.connect
(
&
config
.db
.
clone
()
.unwrap
()
.
uri
)
.await
.map_err
(|
err
|
err
.to_string
())
?
;
Ok
(
Self
{
pool
})
...
...
backend/src/db/pg.rs
View file @
40a1a7b0
...
...
@@ -13,7 +13,7 @@ impl PgDb {
pub
async
fn
new
()
->
Result
<
Self
,
String
>
{
let
pool
=
PgPoolOptions
::
new
()
.max_connections
(
16
)
.connect
(
&
config
.db.uri
)
.connect
(
&
config
.db
.
clone
()
.unwrap
()
.
uri
)
.await
.map_err
(|
err
|
err
.to_string
())
?
;
...
...
backend/src/protocols/socket_io.rs
View file @
40a1a7b0
use
crate
::
commons
::
utils
::{
self
,
CONFIG
as
config
};
use
crate
::
db
::
models
::
game
::
Game
;
use
crate
::
db
::
models
::
log
::
Log
;
use
crate
::
db
::
models
::
manager
::
*
;
use
bytes
::
Bytes
;
use
dashmap
::
DashMap
;
...
...
@@ -13,9 +14,15 @@ use std::collections::HashMap;
use
std
::
sync
::
atomic
::{
AtomicU64
,
Ordering
};
use
std
::
time
::{
Duration
,
Instant
};
#[derive(Default)]
struct
AdminSession
{
current
:
Option
<
(
String
,
SocketRef
)
>
,
version
:
u64
,
}
lazy_static
::
lazy_static!
{
/// 当前唯一在线管理员:(token, socket)。
static
ref
CLIENT_ADMIN_SESSION
:
std
::
sync
::
Mutex
<
Option
<
(
String
,
SocketRef
)
>>
=
std
::
sync
::
Mutex
::
new
(
None
);
static
ref
CLIENT_ADMIN_SESSION
:
std
::
sync
::
Mutex
<
AdminSession
>
=
std
::
sync
::
Mutex
::
new
(
AdminSession
::
default
()
);
static
ref
CLIENT_PLAYER_MAP
:
DashMap
<
String
,
(
i32
,
SocketRef
)
>
=
DashMap
::
new
();
// 1: 正在游戏中
static
ref
ROOM_STATE
:
std
::
sync
::
Mutex
<
RoomState
>
=
std
::
sync
::
Mutex
::
new
(
RoomState
::
default
());
}
...
...
@@ -29,6 +36,8 @@ const ROOM_SCORE_RANK_LIMIT: usize = 10;
const
GAME_START_COUNTDOWN_SECS
:
u64
=
3
;
const
GAME_RESULT_WAIT_SECS
:
u64
=
2
;
const
GAME_RUNNING_SECS
:
u64
=
60
;
//管理员退出5分钟后,关闭所有玩家的连接
const
ADMIN_ABSENT_DEACTIVATE_PLAYER_SECS
:
u64
=
5
*
60
;
fn
get_game_tilte
(
key
:
&
str
)
->
String
{
let
mut
GAME_MAP
=
HashMap
::
new
();
...
...
@@ -70,7 +79,7 @@ struct RankingPlayer {
enum
RoomStatus
{
#[default]
Closed
=
0
,
//关闭
Start
,
//开始排队
Loading
,
//开始排队
Running
,
//进行
Submit
,
//提交结果
Ended
,
//结束
...
...
@@ -199,7 +208,7 @@ impl RoomState {
// fn snapshot(&self) -> Value {
// json!({
// "status": self.status.as_str(),
// "
Start": self.status == RoomStatus::Start
,
// "
Loading": self.status == RoomStatus::Loading
,
// "running": self.status == RoomStatus::Running,
// "ended": self.status == RoomStatus::Ended,
// "room_admin_count": get_admin_count(),
...
...
@@ -220,7 +229,7 @@ impl RoomStatus {
fn
as_str
(
self
)
->
&
'static
str
{
match
self
{
RoomStatus
::
Closed
=>
"closed"
,
RoomStatus
::
Start
=>
"start
"
,
RoomStatus
::
Loading
=>
"loading
"
,
RoomStatus
::
Running
=>
"running"
,
RoomStatus
::
Ended
=>
"ended"
,
RoomStatus
::
Submit
=>
"submit"
,
...
...
@@ -230,13 +239,16 @@ impl RoomStatus {
// 获取玩家数量,不统计管理员
pub
fn
get_player_count
()
->
usize
{
CLIENT_PLAYER_MAP
.len
()
CLIENT_PLAYER_MAP
.iter
()
.filter
(|
player
|
player
.value
()
.
0
>
0
)
.count
()
}
// 获取管理员数量
pub
fn
get_admin_count
()
->
usize
{
CLIENT_ADMIN_SESSION
.lock
()
.map
(|
session
|
usize
::
from
(
session
.is_some
()))
.map
(|
session
|
usize
::
from
(
session
.
current
.
is_some
()))
.unwrap_or
(
0
)
}
...
...
@@ -305,7 +317,7 @@ fn broadcast_msgpack_all_admin(event: &str, payload: &Value) {
let
socket
=
CLIENT_ADMIN_SESSION
.lock
()
.ok
()
.and_then
(|
session
|
session
.as_ref
()
.map
(|(
_
,
socket
)|
socket
.clone
()));
.and_then
(|
session
|
session
.
current
.
as_ref
()
.map
(|(
_
,
socket
)|
socket
.clone
()));
if
let
Some
(
socket
)
=
socket
{
_
=
socket
.emit
(
event
,
&
payload
);
}
...
...
@@ -315,30 +327,57 @@ fn replace_current_admin(userid: String, socket: SocketRef) -> Result<Option<Soc
CLIENT_ADMIN_SESSION
.lock
()
.map
(|
mut
session
|
{
let
old_socket
=
session
.take
()
.map
(|(
_
,
socket
)|
socket
);
*
session
=
Some
((
userid
,
socket
));
let
old_socket
=
session
.current
.take
()
.map
(|(
_
,
socket
)|
socket
);
session
.current
=
Some
((
userid
,
socket
));
session
.version
=
session
.version
.wrapping_add
(
1
);
old_socket
})
.map_err
(|
_
|
())
}
fn
clear_current_admin_if_match
(
userid
:
&
str
,
socket
:
&
SocketRef
)
->
bool
{
fn
clear_current_admin_if_match
(
userid
:
&
str
,
socket
:
&
SocketRef
)
->
Option
<
u64
>
{
CLIENT_ADMIN_SESSION
.lock
()
.map
(|
mut
session
|
{
let
should_clear
=
session
.current
.as_ref
()
.map
(|(
current_userid
,
current_socket
)|
{
current_userid
==
userid
&&
current_socket
.id
==
socket
.id
})
.unwrap_or
(
false
);
if
should_clear
{
*
session
=
None
;
session
.current
=
None
;
session
.version
=
session
.version
.wrapping_add
(
1
);
Some
(
session
.version
)
}
else
{
None
}
should_clear
})
.ok
()
.flatten
()
}
fn
admin_session_still_empty
(
version
:
u64
)
->
bool
{
CLIENT_ADMIN_SESSION
.lock
()
.map
(|
session
|
session
.current
.is_none
()
&&
session
.version
==
version
)
.unwrap_or
(
false
)
}
fn
spawn_deactivate_players_if_admin_absent
(
version
:
u64
)
{
tokio
::
spawn
(
async
move
{
tokio
::
time
::
sleep
(
Duration
::
from_secs
(
ADMIN_ABSENT_DEACTIVATE_PLAYER_SECS
))
.await
;
if
!
admin_session_still_empty
(
version
)
{
return
;
}
for
mut
player
in
CLIENT_PLAYER_MAP
.iter_mut
()
{
player
.value_mut
()
.
0
=
0
;
}
});
}
/// 发送给所有管理员和玩家
fn
broadcast_msgpack_all
(
event
:
&
str
,
payload
:
&
Value
)
{
broadcast_msgpack_all_admin
(
event
,
payload
);
...
...
@@ -375,6 +414,13 @@ fn emit_msgpack_to_user(userid: &str, event: &str, payload: &Value) {
}
}
fn
is_current_player_socket
(
userid
:
&
str
,
socket
:
&
SocketRef
)
->
bool
{
CLIENT_PLAYER_MAP
.get
(
userid
)
.map
(|
player
|
player
.
1
.id
==
socket
.id
)
.unwrap_or
(
false
)
}
fn
push_room_score_ranking
()
->
bool
{
let
(
ranking
,
player_ranks
)
=
if
let
Ok
(
state
)
=
ROOM_STATE
.lock
()
{
let
full_ranking
=
state
.full_score_ranking
();
...
...
@@ -534,16 +580,12 @@ async fn handle_room_command(
}
state
.reset
();
state
.current_game_id
=
Some
(
game_id
);
state
.status
=
RoomStatus
::
Start
;
state
.status
=
RoomStatus
::
Loading
;
// let snapshot = state.snapshot();
drop
(
state
);
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
Some
(
json!
(
index
))));
if
cmd
==
"room_back"
{
broadcast_msgpack_all_player
(
evt
,
&
ok_resp
(
Some
(
json!
(
index
))));
}
else
if
cmd
==
"room_create"
{
broadcast_msgpack_all_player
(
evt
,
&
ok_resp
(
Some
(
json!
(
index
))));
}
}
}
}
...
...
@@ -574,44 +616,32 @@ async fn handle_room_command(
.to_string
();
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
// dbg!(
// &state.current_game_id.clone(),
// &state.status.clone().as_str()
// );
let
Some
(
current_game_id
)
=
state
.current_game_id
.clone
()
else
{
drop
(
state
);
return
;
};
// 当前h5进入的不是大屏开启的游戏房间
if
current_game_id
!=
gameId
{
if
matches!
(
state
.status
,
RoomStatus
::
Closed
)
{
drop
(
state
);
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
&
format!
(
"当前游戏 - {} 房间还未开启"
,
get_game_tilte
(
&
gameId
)),
Some
(
json!
(
2
))
,
None
,
),
);
return
;
}
//游戏不是等待跟正在运行
if
!
matches!
(
state
.status
,
RoomStatus
::
Start
|
RoomStatus
::
Running
)
{
}
else
if
matches!
(
state
.status
,
RoomStatus
::
Running
)
{
// 当前h5进入的不是大屏开启的游戏房间
if
gameId
!=
state
.current_game_id
.clone
()
.unwrap_or_default
(
)
{
drop
(
state
);
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
&
format!
(
"当前游戏 - {} 已经开始了,请在下一场游戏开始前进入"
,
get_game_tilte
(
&
gameId
)
),
Some
(
json!
(
2
)),
&
format!
(
"当前游戏 - {} 房间还未开启"
,
get_game_tilte
(
&
gameId
)),
None
,
),
);
return
;
}
//如果是正在游戏中
let
already_joined
=
state
.players
.contains_key
(
userid
);
//玩家游戏过程中掉线重连
if
already_joined
{
...
...
@@ -628,6 +658,13 @@ async fn handle_room_command(
emit_msgpack
(
&
socket
,
"room_recover_result"
,
&
ok_resp
(
reconnect_payload
));
return
;
}
}
else
if
matches!
(
state
.status
,
RoomStatus
::
Submit
|
RoomStatus
::
Ended
)
{
//如果是在提交或游戏结束阶段断线重连
if
let
Some
(
mut
player
)
=
CLIENT_PLAYER_MAP
.get_mut
(
userid
)
{
player
.
0
=
1
;
}
return
;
}
let
entry_order
=
state
.next_entry_order
;
state
.next_entry_order
+=
1
;
...
...
@@ -654,7 +691,7 @@ async fn handle_room_command(
if
let
Some
(
mut
player
)
=
CLIENT_PLAYER_MAP
.get_mut
(
userid
)
{
player
.
0
=
1
;
}
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
Some
(
json!
(
current_game_i
d
))));
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
Some
(
json!
(
gameI
d
))));
// 给所有管理员广播房间状态更新
broadcast_msgpack_all_admin
(
evt
,
...
...
@@ -673,7 +710,7 @@ async fn handle_room_command(
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"房间中没有玩家"
,
Some
(
json!
(
1
))));
return
;
}
if
state
.status
!=
RoomStatus
::
Start
{
if
state
.status
!=
RoomStatus
::
Loading
{
drop
(
state
);
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"房间没有创建"
,
Some
(
json!
(
2
))));
return
;
...
...
@@ -708,7 +745,6 @@ async fn handle_room_command(
if
!
state
.players
.contains_key
(
userid
)
{
drop
(
state
);
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"您不在房间中"
,
None
));
_
=
socket
.clone
()
.disconnect
();
return
;
}
else
if
!
matches!
(
state
.status
,
RoomStatus
::
Running
)
{
drop
(
state
);
...
...
@@ -789,6 +825,23 @@ async fn handle_room_command(
}
else
{
(
score
,
rank
,
Vec
::
new
())
};
let
action
=
async
|
msg
:
&
str
|
{
let
log
=
Log
{
id
:
0
,
msg
:
msg
.to_string
(),
params
:
json!
({
"wechat_id"
:
&
wechat
.to_string
(),
"nickname"
:
&
nickname
.to_string
(),
"avatar"
:
&
avatar
.to_string
(),
"item_num"
:
item_num
as
i32
,
"game_rank"
:
rank
as
i32
,
"score"
:
score
as
i32
,
}),
create_date
:
None
,
};
return
log
.insert
()
.await
;
};
let
game
=
Game
{
wechat_id
:
wechat
.to_string
(),
nickname
:
nickname
.to_string
(),
...
...
@@ -798,7 +851,20 @@ async fn handle_room_command(
score
:
score
as
i32
,
create_date
:
None
,
};
if
game
.insert
()
.await
{
//还需要提交给后台
// match utils::post(wechat, item_num).await {
// Ok(res) => {
// if let Some(msg) = res {
// // action(&msg).await;
// }
// }
// Err(err) => {
// dbg!("err", &err);
// }
// };
emit_msgpack
(
&
socket
,
evt
,
...
...
@@ -936,19 +1002,24 @@ pub async fn on_connect(_io: SocketIo, socket: SocketRef, Data(data): Data<Value
}
}
else
{
if
get_admin_count
()
==
0
{
match
CLIENT_PLAYER_MAP
.entry
(
userid
.clone
())
{
Entry
::
Occupied
(
mut
entry
)
=>
{
entry
.insert
((
0
,
socket
.clone
()));
}
Entry
::
Vacant
(
entry
)
=>
{
entry
.insert
((
0
,
socket
.clone
()));
}
}
emit_msgpack
(
&
socket
,
"connect_result"
,
&
err_resp
(
"游戏管理员暂未开启该房间"
,
None
),
);
_
=
socket
.disconnect
();
return
;
}
match
CLIENT_PLAYER_MAP
.entry
(
userid
.clone
())
{
Entry
::
Occupied
(
mut
entry
)
=>
{
// 同一玩家重连时用新 socket 接管,避免网络抖动后无法回到本局。
let
old_socket
=
entry
.get
()
.
1
.clone
();
_
=
old_socket
.disconnect
();
// 同一玩家重连时用新 socket 接管,旧 socket 后续消息会被忽略。
entry
.insert
((
0
,
socket
.clone
()));
}
Entry
::
Vacant
(
entry
)
=>
{
...
...
@@ -958,6 +1029,7 @@ pub async fn on_connect(_io: SocketIo, socket: SocketRef, Data(data): Data<Value
}
// 鉴权通过后,在这里挂玩家消息事件处理器。
let
message_userid
=
userid
.clone
();
let
message_is_admin
=
is_admin
;
socket
.on
(
"message"
,
move
|
socket
:
SocketRef
,
data
:
Data
<
Bytes
>
|
{
let
message_userid
=
message_userid
.clone
();
async
move
{
...
...
@@ -971,7 +1043,10 @@ pub async fn on_connect(_io: SocketIo, socket: SocketRef, Data(data): Data<Value
if
let
(
Some
(
cmd
),
Some
(
data
))
=
(
data
.get
(
"cmd"
)
.and_then
(|
v
|
v
.as_str
()),
data
.get
(
"data"
))
{
handle_room_command
(
&
socket
,
&
message_userid
,
is_admin
,
cmd
,
&
data
)
.await
;
if
!
message_is_admin
&&
!
is_current_player_socket
(
&
message_userid
,
&
socket
)
{
return
;
}
handle_room_command
(
&
socket
,
&
message_userid
,
message_is_admin
,
cmd
,
&
data
)
.await
;
}
}
});
...
...
@@ -980,15 +1055,18 @@ pub async fn on_connect(_io: SocketIo, socket: SocketRef, Data(data): Data<Value
move
|
socket
:
SocketRef
,
_reason
:
DisconnectReason
|
async
move
{
if
!
is_create_user
{
if
is_admin
{
if
!
clear_current_admin_if_match
(
&
userid
,
&
socket
)
{
let
Some
(
admin_session_version
)
=
clear_current_admin_if_match
(
&
userid
,
&
socket
)
else
{
return
;
}
}
;
// 前端页面刷新时房间状态丢失体验会不好
if
get_admin_count
()
==
0
{
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
state
.reset
();
// 如果管理员都退出了,就把房间状态重置。
drop
(
state
);
}
spawn_deactivate_players_if_admin_absent
(
admin_session_version
);
}
}
else
{
let
should_remove
=
CLIENT_PLAYER_MAP
...
...
@@ -1000,7 +1078,7 @@ pub async fn on_connect(_io: SocketIo, socket: SocketRef, Data(data): Data<Value
}
CLIENT_PLAYER_MAP
.remove
(
&
userid
);
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
if
state
.status
!=
RoomStatus
::
Start
{
if
state
.status
!=
RoomStatus
::
Loading
{
if
let
Some
(
player
)
=
state
.players
.get_mut
(
&
userid
)
{
player
.online
=
false
;
}
...
...
@@ -1079,7 +1157,7 @@ pub async fn login(req: &mut Request, res: &mut Response) {
let
game_id
=
state
.current_game_id
.clone
()
.unwrap_or
(
""
.to_string
());
if
matches!
(
state
.status
,
RoomStatus
::
Start
|
RoomStatus
::
Running
|
RoomStatus
::
Submit
RoomStatus
::
Loading
|
RoomStatus
::
Running
|
RoomStatus
::
Submit
)
{
drop
(
state
);
res
.render
(
Json
(
err_resp
(
...
...
frontend-h5/src/commons/utils.ts
View file @
40a1a7b0
...
...
@@ -63,6 +63,233 @@ export function $format_str(str: string, num: number) {
return
str
.
substring
(
0
,
num
)
+
'...'
;
}
let
toastElement
:
HTMLDivElement
|
undefined
;
let
toastTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
;
export
function
$toast
(
message
?:
string
,
duration
=
2000
)
{
if
(
!
message
)
{
return
;
}
if
(
!
toastElement
)
{
toastElement
=
document
.
createElement
(
'div'
);
toastElement
.
style
.
cssText
=
[
'position: fixed'
,
'left: 50%'
,
'top: 45%'
,
'z-index: 99999'
,
'max-width: 560px'
,
'padding: 18px 28px'
,
'border-radius: 18px'
,
'background: rgba(0, 0, 0, 0.72)'
,
'color: #fff'
,
'font-size: 22px'
,
'line-height: 1.4'
,
'text-align: center'
,
'transform: translate(-50%, -40%)'
,
'opacity: 0'
,
'pointer-events: none'
,
'transition: opacity 0.2s ease, transform 0.2s ease'
,
].
join
(
';'
);
document
.
body
.
appendChild
(
toastElement
);
}
if
(
toastTimer
)
{
window
.
clearTimeout
(
toastTimer
);
toastTimer
=
undefined
;
}
toastElement
.
textContent
=
message
;
toastElement
.
style
.
opacity
=
'1'
;
toastElement
.
style
.
transform
=
'translate(-50%, -50%)'
;
toastTimer
=
window
.
setTimeout
(()
=>
{
if
(
!
toastElement
)
{
return
;
}
toastElement
.
style
.
opacity
=
'0'
;
toastElement
.
style
.
transform
=
'translate(-50%, -40%)'
;
toastTimer
=
undefined
;
},
duration
);
}
type
ConfirmOptions
=
{
title
?:
string
;
message
?:
string
;
confirmText
?:
string
;
cancelText
?:
string
;
}
let
confirmMaskElement
:
HTMLDivElement
|
undefined
;
let
confirmBoxElement
:
HTMLDivElement
|
undefined
;
let
confirmTitleElement
:
HTMLDivElement
|
undefined
;
let
confirmMessageElement
:
HTMLDivElement
|
undefined
;
let
confirmCancelElement
:
HTMLButtonElement
|
undefined
;
let
confirmOkElement
:
HTMLButtonElement
|
undefined
;
let
confirmResolve
:
((
value
:
boolean
)
=>
void
)
|
undefined
;
let
confirmHideTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
;
function
closeConfirm
(
result
:
boolean
)
{
if
(
!
confirmMaskElement
||
!
confirmBoxElement
)
{
confirmResolve
?.(
result
);
confirmResolve
=
undefined
;
return
;
}
if
(
confirmHideTimer
)
{
window
.
clearTimeout
(
confirmHideTimer
);
confirmHideTimer
=
undefined
;
}
confirmMaskElement
.
style
.
opacity
=
'0'
;
confirmBoxElement
.
style
.
transform
=
'scale(0.96)'
;
confirmHideTimer
=
window
.
setTimeout
(()
=>
{
if
(
confirmMaskElement
)
{
confirmMaskElement
.
style
.
display
=
'none'
;
}
confirmHideTimer
=
undefined
;
},
200
);
confirmResolve
?.(
result
);
confirmResolve
=
undefined
;
}
function
ensureConfirmElement
()
{
if
(
confirmMaskElement
)
{
return
;
}
confirmMaskElement
=
document
.
createElement
(
'div'
);
confirmMaskElement
.
style
.
cssText
=
[
'position: fixed'
,
'inset: 0'
,
'z-index: 99998'
,
'display: none'
,
'align-items: center'
,
'justify-content: center'
,
'background: rgba(0, 0, 0, 0.45)'
,
'opacity: 0'
,
'transition: opacity 0.2s ease'
,
].
join
(
';'
);
confirmBoxElement
=
document
.
createElement
(
'div'
);
confirmBoxElement
.
style
.
cssText
=
[
'width: min(560px, calc(100vw - 80px))'
,
'box-sizing: border-box'
,
'padding: 34px 32px 28px'
,
'border-radius: 22px'
,
'background: #fff'
,
'color: #333'
,
'font-size: 24px'
,
'line-height: 1.45'
,
'text-align: center'
,
'box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22)'
,
'transform: scale(0.96)'
,
'transition: transform 0.2s ease'
,
].
join
(
';'
);
confirmTitleElement
=
document
.
createElement
(
'div'
);
confirmTitleElement
.
style
.
cssText
=
[
'margin-bottom: 16px'
,
'font-size: 30px'
,
'font-weight: 700'
,
'color: #222'
,
].
join
(
';'
);
confirmMessageElement
=
document
.
createElement
(
'div'
);
confirmMessageElement
.
style
.
cssText
=
[
'min-height: 34px'
,
'word-break: break-word'
,
].
join
(
';'
);
const
buttonWrap
=
document
.
createElement
(
'div'
);
buttonWrap
.
style
.
cssText
=
[
'display: flex'
,
'gap: 18px'
,
'margin-top: 30px'
,
].
join
(
';'
);
confirmCancelElement
=
document
.
createElement
(
'button'
);
confirmCancelElement
.
type
=
'button'
;
confirmCancelElement
.
style
.
cssText
=
[
'flex: 1'
,
'height: 58px'
,
'border: 1px solid #ddd'
,
'border-radius: 29px'
,
'background: #fff'
,
'color: #666'
,
'font-size: 24px'
,
].
join
(
';'
);
confirmCancelElement
.
onclick
=
()
=>
closeConfirm
(
false
);
confirmOkElement
=
document
.
createElement
(
'button'
);
confirmOkElement
.
type
=
'button'
;
confirmOkElement
.
style
.
cssText
=
[
'flex: 1'
,
'height: 58px'
,
'border: 0'
,
'border-radius: 29px'
,
'background: #AA0000'
,
'color: #fff'
,
'font-size: 24px'
,
].
join
(
';'
);
confirmOkElement
.
onclick
=
()
=>
closeConfirm
(
true
);
confirmMaskElement
.
onclick
=
(
event
)
=>
{
if
(
event
.
target
===
confirmMaskElement
)
{
closeConfirm
(
false
);
}
};
buttonWrap
.
append
(
confirmCancelElement
,
confirmOkElement
);
confirmBoxElement
.
append
(
confirmTitleElement
,
confirmMessageElement
,
buttonWrap
);
confirmMaskElement
.
appendChild
(
confirmBoxElement
);
document
.
body
.
appendChild
(
confirmMaskElement
);
}
export
function
$confirm
(
message
?:
string
,
title
?:
string
):
Promise
<
boolean
>
;
export
function
$confirm
(
options
?:
ConfirmOptions
):
Promise
<
boolean
>
;
export
function
$confirm
(
messageOrOptions
?:
string
|
ConfirmOptions
,
title
?:
string
):
Promise
<
boolean
>
{
const
options
:
ConfirmOptions
=
typeof
messageOrOptions
===
'object'
?
messageOrOptions
:
{
message
:
messageOrOptions
,
title
};
if
(
!
options
.
message
)
{
return
Promise
.
resolve
(
false
);
}
ensureConfirmElement
();
if
(
confirmResolve
)
{
confirmResolve
(
false
);
confirmResolve
=
undefined
;
}
if
(
confirmHideTimer
)
{
window
.
clearTimeout
(
confirmHideTimer
);
confirmHideTimer
=
undefined
;
}
confirmTitleElement
!
.
textContent
=
options
.
title
??
'提示'
;
confirmMessageElement
!
.
textContent
=
options
.
message
;
confirmCancelElement
!
.
textContent
=
options
.
cancelText
??
'取消'
;
confirmOkElement
!
.
textContent
=
options
.
confirmText
??
'确定'
;
confirmMaskElement
!
.
style
.
display
=
'flex'
;
window
.
requestAnimationFrame
(()
=>
{
if
(
!
confirmMaskElement
||
!
confirmBoxElement
)
{
return
;
}
confirmMaskElement
.
style
.
opacity
=
'1'
;
confirmBoxElement
.
style
.
transform
=
'scale(1)'
;
});
return
new
Promise
<
boolean
>
((
resolve
)
=>
{
confirmResolve
=
resolve
;
});
}
export
function
$is_run_local
():
boolean
{
if
(
/https
?
:
\/\/
localhost|192.168/gim
.
test
(
location
.
href
))
{
return
true
;
...
...
@@ -103,3 +330,38 @@ export function $getWechat(): any | null {
return
null
;
}
export
async
function
postJson
(
token
:
string
,
index
:
number
)
{
const
url
=
"https://api.guocai365.org.cn/adminapi/offline_clearance/score/list"
;
let
arr
=
[
''
,
'JGQF'
,
'JBJF'
,
'MSYF'
,
'CMYF'
,
'QCNF'
,
'FYDT'
];
try
{
const
resp
=
await
fetch
(
url
,
{
method
:
"POST"
,
headers
:
{
"Authorization"
:
`Bearer
${
token
}
`
,
"Content-Type"
:
"application/json"
,
},
body
:
JSON
.
stringify
({
activity_id
:
1
,
game_code
:
arr
[
index
],
page
:
1
,
pageSize
:
20
,
}),
});
const
text
=
await
resp
.
text
();
if
(
!
resp
.
ok
)
{
console
.
error
(
"请求失败:"
,
resp
.
status
,
text
);
return
;
}
// 如果返回的是 JSON
const
data
=
JSON
.
parse
(
text
);
console
.
log
(
"响应:"
,
data
);
}
catch
(
err
)
{
console
.
error
(
"请求异常:"
,
err
);
}
}
\ No newline at end of file
frontend-h5/src/commons/ws.ts
View file @
40a1a7b0
...
...
@@ -32,15 +32,15 @@ export function initSocket(args: SocketInitArgs): Socket | null {
onSocketMessage
(
args
.
onMessage
);
}
if
(
socket
)
{
if
(
socket
.
connected
)
{
args
.
onConnect
?.(
socket
);
return
socket
;
}
socket
.
removeAllListeners
();
socket
.
disconnect
();
socket
=
null
;
}
//
if (socket) {
//
if (socket.connected) {
//
args.onConnect?.(socket);
//
return socket;
//
}
//
socket.removeAllListeners();
//
socket.disconnect();
//
socket = null;
//
}
if
(
$is_run_local
())
{
console
.
log
(
'socket.io init:'
,
{
...
...
@@ -68,7 +68,7 @@ export function initSocket(args: SocketInitArgs): Socket | null {
socket
.
onAny
((
evt
,
payload
)
=>
{
const
data
=
decode
(
payload
);
if
(
$is_run_local
())
{
console
.
log
(
"
recv<============
"
,
evt
,
data
);
console
.
log
(
"
============>recv
"
,
evt
,
data
);
}
messageHandlers
.
forEach
((
handler
)
=>
handler
(
evt
,
data
));
});
...
...
@@ -95,7 +95,7 @@ export function sendSocketMessage(cmd: string, data: any = {}): boolean {
return
false
;
}
if
(
$is_run_local
())
{
console
.
log
(
'
================>send
'
,
cmd
,
data
);
console
.
log
(
'
send<================
'
,
cmd
,
data
);
}
socket
.
send
(
encode
({
cmd
,
data
}));
return
true
;
...
...
@@ -114,7 +114,7 @@ export function emitSocket(eventName: string, data?: any): boolean {
return
true
;
}
export
function
closeSocket
():
void
{
socket
?.
disconnect
();
socket
=
null
;
}
//
export function closeSocket(): void {
//
socket?.disconnect();
//
socket = null;
//
}
frontend-h5/src/composables/useGameSocket.ts
View file @
40a1a7b0
...
...
@@ -2,7 +2,7 @@ import { ref } from 'vue'
import
{
useRouter
}
from
'vue-router'
import
type
{
Socket
}
from
'socket.io-client'
import
{
initSocket
,
onSocketMessage
,
sendSocketMessage
}
from
'@/commons/ws'
import
{
$read
}
from
'@/commons/utils'
import
{
$read
,
$toast
,
postJson
}
from
'@/commons/utils'
type
SocketPayload
=
{
msg
?:
string
...
...
@@ -76,11 +76,16 @@ export function useGameSocket(options: GameSocketOptions) {
if
(
state
===
0
)
{
if
(
evt
===
'room_join_result'
)
{
switch
(
data
)
{
case
1
:
userJoinStatus
.
value
=
true
;
break
;
case
2
:
case
1
:
userJoinStatus
.
value
=
true
;
return
;
case
2
:
{
// $toast(msg)
alert
(
msg
)
return
;
}
case
3
:
alert
(
msg
);
break
;
// alert(msg);
$toast
(
msg
)
return
;
}
}
else
if
(
evt
==
'submit_score_save'
)
{
//提交分数的情况下有问题的情况下,只重试3次
...
...
@@ -102,7 +107,8 @@ export function useGameSocket(options: GameSocketOptions) {
}
if
(
state
===
0
)
{
alert
(
msg
)
// alert(msg)
$toast
(
msg
)
return
}
...
...
@@ -113,7 +119,9 @@ export function useGameSocket(options: GameSocketOptions) {
}
case
'room_create_result'
:
{
if
(
`game
${
data
}
`
==
options
.
gameId
)
{
location
.
reload
();
// location.reload();
options
.
onConnect
?.();
// debugger
return
;
}
const
nextPath
=
getGameHomePath
(
data
)
...
...
@@ -125,20 +133,14 @@ export function useGameSocket(options: GameSocketOptions) {
}
case
'room_join_result'
:
{
if
(
data
!=
options
.
gameId
)
{
debugger
return
;
}
$toast
(
'您已进入本游戏房间'
)
userJoinStatus
.
value
=
true
break
}
case
'room_recover_result'
:
{
/*{
"gameId": "game6",
"rank": 1,
"reconnect": true,
"remainingSeconds": 56,
"score": 0,
"status": "running"
}*/
//游戏进行中玩家掉线重连时
if
(
data
.
gameId
!=
options
.
gameId
)
{
return
;
...
...
@@ -160,6 +162,7 @@ export function useGameSocket(options: GameSocketOptions) {
break
}
case
'submit_score_save_result'
:
{
// postJson()
options
.
onScoreSubmitted
?.(
true
,
data
)
break
}
...
...
frontend-h5/src/pages/game1/Game.vue
View file @
40a1a7b0
...
...
@@ -6,7 +6,7 @@ import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from '
import
LoadingView
from
'./views/LoadingView.vue'
import
PlayingView
from
'./views/PlayingView.vue'
import
ScoreView
from
'./views/ScoreView.vue'
import
{
$getWechat
}
from
'@/commons/utils.ts'
import
{
$getWechat
,
$toast
}
from
'@/commons/utils.ts'
import
{
playGame1Music
}
from
'@/commons/music'
type
GameView
=
'loading'
|
'playing'
|
'score'
...
...
@@ -212,7 +212,8 @@ if (wechat) {
stopGameCountdown
()
currentView
.
value
=
'loading'
setDivDescVisible
(
false
)
alert
(
'管理员关闭了该房间'
)
// alert('管理员关闭了该房间')
$toast
(
'管理员关闭了该房间'
)
},
onRoomBack
:
()
=>
{
resetToLoadingView
()
...
...
@@ -282,14 +283,15 @@ const showGameRuleHandler = () => {
background
:
v-bind
(
'imageUrls.rule'
)
center
/
cover
no-repeat
;
width
:
692px
;
height
:
797px
;
margin-top
:
-20%
;
transform
:
scale
(
0.5
);
.rule-title
{
text-align
:
center
;
color
:
white
;
font-weight
:
bold
;
font-size
:
2
2
pt
;
margin-top
:
10px
;
font-size
:
2
6
pt
;
line-height
:
50pt
;
letter-spacing
:
4px
;
}
...
...
frontend-h5/src/pages/game1/views/LoadingView.vue
View file @
40a1a7b0
...
...
@@ -40,17 +40,18 @@ onMounted(() => {
.game-desc
{
position
:
absolute
;
top
:
5
0%
;
top
:
4
0%
;
left
:
var
(
--stage-viewport-left
,
0
);
display
:
flex
;
width
:
86px
;
height
:
256px
;
width
:
58px
;
height
:
158px
;
padding-top
:
10px
;
align-items
:
center
;
justify-content
:
center
;
border-radius
:
0
12px
12px
0
;
background
:
rgba
(
40
,
12
,
8
,
0.52
);
color
:
white
;
font-size
:
32pt
;
color
:
rgba
(
255
,
255
,
255
,
0.9
)
;
font-size
:
25px
;
letter-spacing
:
10px
;
line-height
:
1.25
;
text-align
:
center
;
...
...
frontend-h5/src/pages/game4/Game4.vue
View file @
40a1a7b0
...
...
@@ -6,7 +6,7 @@ import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from '
import
LoadingView
from
'./views/LoadingView.vue'
import
PlayingView
from
'./views/PlayingView.vue'
import
ScoreView
from
'./views/ScoreView.vue'
import
{
$getWechat
}
from
'@/commons/utils.ts'
import
{
$getWechat
,
$toast
}
from
'@/commons/utils.ts'
const
gameId
=
'game4'
;
type
GameView
=
'loading'
|
'playing'
|
'score'
...
...
@@ -123,7 +123,8 @@ function showLoadingView() {
stopGameCountdown
()
currentView
.
value
=
'loading'
setDivDescVisible
(
false
)
alert
(
'管理员关闭了该房间'
)
// alert('管理员关闭了该房间')
$toast
(
'管理员关闭了该房间'
)
}
function
resetToLoadingView
()
{
...
...
@@ -266,14 +267,15 @@ onBeforeUnmount(() => {
background
:
v-bind
(
'imageUrls.rule'
)
center
/
cover
no-repeat
;
width
:
692px
;
height
:
797px
;
margin-top
:
-20%
;
transform
:
scale
(
0.5
);
.rule-title
{
text-align
:
center
;
color
:
white
;
font-weight
:
bold
;
font-size
:
2
2
pt
;
margin-top
:
10px
;
font-size
:
2
6
pt
;
line-height
:
50pt
;
letter-spacing
:
4px
;
}
...
...
frontend-h5/src/pages/game5/Game5.vue
View file @
40a1a7b0
...
...
@@ -6,7 +6,7 @@ import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from '
import
LoadingView
from
'./views/LoadingView.vue'
import
PlayingView
from
'./views/PlayingView.vue'
import
ScoreView
from
'./views/ScoreView.vue'
import
{
$getWechat
}
from
'@/commons/utils.ts'
import
{
$getWechat
,
$toast
}
from
'@/commons/utils.ts'
import
{
playGame1Music
}
from
'@/commons/music'
const
gameId
=
'game5'
;
...
...
@@ -150,7 +150,8 @@ function showLoadingView() {
stopGameCountdown
()
currentView
.
value
=
'loading'
setDivDescVisible
(
false
)
alert
(
'管理员关闭了该房间'
)
// alert('管理员关闭了该房间')
$toast
(
'管理员关闭了该房间'
)
}
function
resetToLoadingView
()
{
...
...
@@ -349,14 +350,15 @@ const rankCloseHandler = () => {
background
:
v-bind
(
'imageUrls.rule'
)
center
/
cover
no-repeat
;
width
:
692px
;
height
:
797px
;
margin-top
:
-20%
;
transform
:
scale
(
0.5
);
.rule-title
{
text-align
:
center
;
color
:
white
;
font-weight
:
bold
;
font-size
:
2
2
pt
;
margin-top
:
10px
;
font-size
:
2
6
pt
;
line-height
:
50pt
;
letter-spacing
:
4px
;
}
...
...
frontend-h5/src/pages/game5/views/LoadingView.vue
View file @
40a1a7b0
...
...
@@ -42,17 +42,18 @@ onMounted(() => {
.game-desc
{
position
:
absolute
;
top
:
5
0%
;
top
:
4
0%
;
left
:
var
(
--stage-viewport-left
,
0
);
display
:
flex
;
width
:
86px
;
height
:
256px
;
width
:
58px
;
height
:
158px
;
padding-top
:
10px
;
align-items
:
center
;
justify-content
:
center
;
border-radius
:
0
12px
12px
0
;
background
:
rgba
(
40
,
12
,
8
,
0.52
);
color
:
white
;
font-size
:
32pt
;
color
:
rgba
(
255
,
255
,
255
,
0.9
)
;
font-size
:
25px
;
letter-spacing
:
10px
;
line-height
:
1.25
;
text-align
:
center
;
...
...
@@ -114,6 +115,7 @@ onMounted(() => {
text-align
:
center
;
background
:
v-bind
(
'imageUrls.contdown'
)
center
/
cover
;
}
.txt-nickname
{
position
:
absolute
;
bottom
:
10%
;
...
...
@@ -146,15 +148,15 @@ onMounted(() => {
color
:
white
;
position
:
absolute
;
z-index
:
-1
;
top
:
786px
;
top
:
786px
;
left
:
50%
;
transform
:
translate
(
-50%
);
line-height
:
118px
;
text-align
:
center
;
font-weight
:
500
;
font-size
:
46px
;
color
:
#FFFFFF
;
letter-spacing
:
5px
;
font-size
:
46px
;
color
:
#FFFFFF
;
letter-spacing
:
5px
;
background
:
v-bind
(
'imageUrls.startBg'
)
center
/
cover
;
}
...
...
frontend-h5/src/pages/game6/Game.vue
View file @
40a1a7b0
...
...
@@ -5,7 +5,7 @@ import MobileStage from '@/components/MobileStage.vue'
import
{
useGameSocket
,
joinSharedRoom
,
sendGameMessage
,
userJoinStatus
}
from
'@/composables/useGameSocket'
import
LoadingView
from
'./views/LoadingView.vue'
import
PlayingView
from
'./views/PlayingView.vue'
import
{
$getWechat
}
from
'@/commons/utils.ts'
import
{
$getWechat
,
$toast
}
from
'@/commons/utils.ts'
import
{
playGame6Music
,
stopGame6Music
}
from
'@/commons/music'
;
type
GameView
=
'loading'
|
'playing'
...
...
@@ -244,7 +244,7 @@ if (wechat) {
stopGameCountdown
()
currentView
.
value
=
'loading'
setDivDescVisible
(
false
)
aler
t
(
'管理员关闭了该房间'
)
$toas
t
(
'管理员关闭了该房间'
)
},
onRoomBack
:
()
=>
{
resetToLoadingView
()
...
...
@@ -358,14 +358,15 @@ const rankCloseHandler = () => {
background
:
v-bind
(
'imageUrls.rule'
)
center
/
cover
no-repeat
;
width
:
692px
;
height
:
797px
;
margin-top
:
-20%
;
transform
:
scale
(
0.5
);
.rule-title
{
text-align
:
center
;
color
:
white
;
font-weight
:
bold
;
font-size
:
2
2
pt
;
margin-top
:
10px
;
font-size
:
2
6
pt
;
line-height
:
50pt
;
letter-spacing
:
4px
;
}
...
...
frontend-h5/src/pages/game6/views/LoadingView.vue
View file @
40a1a7b0
...
...
@@ -77,18 +77,18 @@ onMounted(() => {
.game-desc
{
position
:
absolute
;
z-index
:
9999999999
;
top
:
50%
;
top
:
40%
;
left
:
var
(
--stage-viewport-left
,
0
);
display
:
flex
;
width
:
86px
;
height
:
256px
;
width
:
58px
;
height
:
158px
;
padding-top
:
10px
;
align-items
:
center
;
justify-content
:
center
;
border-radius
:
0
12px
12px
0
;
background
:
rgba
(
40
,
12
,
8
,
0.52
);
color
:
white
;
font-size
:
32pt
;
color
:
rgba
(
255
,
255
,
255
,
0.9
)
;
font-size
:
25px
;
letter-spacing
:
10px
;
line-height
:
1.25
;
text-align
:
center
;
...
...
frontend-large/src/assets/images/game1/podium.png
View replaced file @
a8efe97a
View file @
40a1a7b0
386 KB
|
W:
|
H:
151 KB
|
W:
|
H:
2-up
Swipe
Onion skin
frontend-large/src/commons/music.ts
View file @
40a1a7b0
...
...
@@ -23,17 +23,28 @@ export function stopAllMusic() {
backgroundMusic
=
null
;
}
function
playAudioTimes
(
audio
:
HTMLAudioElement
,
times
:
number
)
{
let
count
=
1
audio
.
currentTime
=
0
audio
.
play
()
audio
.
onended
=
()
=>
{
if
(
count
>=
times
)
{
audio
.
onended
=
null
return
}
count
++
audio
.
currentTime
=
0
audio
.
play
()
}
}
function
getBackgroundMusic
(
music
:
string
,
force
:
boolean
=
true
)
{
if
(
force
)
{
//
if
(
backgroundMusic
)
{
stopApplauseMusic
();
stopGame1Music
();
// stopGame2Music();
// stopGame3Music();
stopGame4Music
();
stopGame5Music
();
stopGame6Music
();
backgroundMusic
=
null
;
stopAllMusic
();
}
}
if
(
!
backgroundMusic
)
{
...
...
@@ -47,13 +58,12 @@ function getBackgroundMusic(music: string, force: boolean = true) {
return
backgroundMusic
}
//播放庆祝音乐
export
async
function
playApplauseMusic
()
{
const
audio
=
getBackgroundMusic
(
applause
)
try
{
await
audio
.
play
()
window
.
removeEventListener
(
'pointerdown'
,
playApplauseMusic
)
window
.
removeEventListener
(
'keydown'
,
playApplauseMusic
)
playAudioTimes
(
audio
,
2
);
//播放两次停止
}
catch
{
window
.
addEventListener
(
'pointerdown'
,
playApplauseMusic
,
{
once
:
false
})
window
.
addEventListener
(
'keydown'
,
playApplauseMusic
,
{
once
:
false
})
...
...
frontend-large/src/commons/utils.ts
View file @
40a1a7b0
...
...
@@ -94,3 +94,232 @@ export function $query(q: string | string[]): string | string[] {
}
return
params
.
get
(
q
)
||
''
;
}
let
toastElement
:
HTMLDivElement
|
undefined
;
let
toastTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
;
export
function
$toast
(
message
?:
string
,
duration
=
2000
)
{
if
(
!
message
)
{
return
;
}
if
(
!
toastElement
)
{
toastElement
=
document
.
createElement
(
'div'
);
toastElement
.
style
.
cssText
=
[
'position: fixed'
,
'left: 50%'
,
'top: 45%'
,
'z-index: 99999'
,
'max-width: 560px'
,
'padding: 18px 28px'
,
'border-radius: 18px'
,
'background: rgba(0, 0, 0, 0.72)'
,
'color: #fff'
,
'font-size: 22px'
,
'line-height: 1.4'
,
'text-align: center'
,
'transform: translate(-50%, -40%)'
,
'opacity: 0'
,
'pointer-events: none'
,
'transition: opacity 0.2s ease, transform 0.2s ease'
,
].
join
(
';'
);
document
.
body
.
appendChild
(
toastElement
);
}
if
(
toastTimer
)
{
window
.
clearTimeout
(
toastTimer
);
toastTimer
=
undefined
;
}
toastElement
.
textContent
=
message
;
toastElement
.
style
.
opacity
=
'1'
;
toastElement
.
style
.
transform
=
'translate(-50%, -50%)'
;
toastTimer
=
window
.
setTimeout
(()
=>
{
if
(
!
toastElement
)
{
return
;
}
toastElement
.
style
.
opacity
=
'0'
;
toastElement
.
style
.
transform
=
'translate(-50%, -40%)'
;
toastTimer
=
undefined
;
},
duration
);
}
type
ConfirmOptions
=
{
title
?:
string
;
message
?:
string
;
confirmText
?:
string
;
cancelText
?:
string
;
}
let
confirmMaskElement
:
HTMLDivElement
|
undefined
;
let
confirmBoxElement
:
HTMLDivElement
|
undefined
;
let
confirmTitleElement
:
HTMLDivElement
|
undefined
;
let
confirmMessageElement
:
HTMLDivElement
|
undefined
;
let
confirmCancelElement
:
HTMLButtonElement
|
undefined
;
let
confirmOkElement
:
HTMLButtonElement
|
undefined
;
let
confirmResolve
:
((
value
:
boolean
)
=>
void
)
|
undefined
;
let
confirmHideTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
;
function
closeConfirm
(
result
:
boolean
)
{
if
(
!
confirmMaskElement
||
!
confirmBoxElement
)
{
confirmResolve
?.(
result
);
confirmResolve
=
undefined
;
return
;
}
if
(
confirmHideTimer
)
{
window
.
clearTimeout
(
confirmHideTimer
);
confirmHideTimer
=
undefined
;
}
confirmMaskElement
.
style
.
opacity
=
'0'
;
confirmBoxElement
.
style
.
transform
=
'scale(0.96)'
;
confirmHideTimer
=
window
.
setTimeout
(()
=>
{
if
(
confirmMaskElement
)
{
confirmMaskElement
.
style
.
display
=
'none'
;
}
confirmHideTimer
=
undefined
;
},
200
);
confirmResolve
?.(
result
);
confirmResolve
=
undefined
;
}
function
ensureConfirmElement
()
{
if
(
confirmMaskElement
)
{
return
;
}
confirmMaskElement
=
document
.
createElement
(
'div'
);
confirmMaskElement
.
style
.
cssText
=
[
'position: fixed'
,
'inset: 0'
,
'z-index: 99998'
,
'display: none'
,
'align-items: center'
,
'justify-content: center'
,
'background: rgba(0, 0, 0, 0.45)'
,
'opacity: 0'
,
'transition: opacity 0.2s ease'
,
].
join
(
';'
);
confirmBoxElement
=
document
.
createElement
(
'div'
);
confirmBoxElement
.
style
.
cssText
=
[
'width: min(560px, calc(100vw - 80px))'
,
'box-sizing: border-box'
,
'padding: 34px 32px 28px'
,
'border-radius: 22px'
,
'background: #fff'
,
'color: #333'
,
'font-size: 24px'
,
'line-height: 1.45'
,
'text-align: center'
,
'box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22)'
,
'transform: scale(0.96)'
,
'transition: transform 0.2s ease'
,
].
join
(
';'
);
confirmTitleElement
=
document
.
createElement
(
'div'
);
confirmTitleElement
.
style
.
cssText
=
[
'margin-bottom: 16px'
,
'font-size: 30px'
,
'font-weight: 700'
,
'color: #222'
,
].
join
(
';'
);
confirmMessageElement
=
document
.
createElement
(
'div'
);
confirmMessageElement
.
style
.
cssText
=
[
'min-height: 34px'
,
'word-break: break-word'
,
].
join
(
';'
);
const
buttonWrap
=
document
.
createElement
(
'div'
);
buttonWrap
.
style
.
cssText
=
[
'display: flex'
,
'gap: 18px'
,
'margin-top: 30px'
,
].
join
(
';'
);
confirmCancelElement
=
document
.
createElement
(
'button'
);
confirmCancelElement
.
type
=
'button'
;
confirmCancelElement
.
style
.
cssText
=
[
'flex: 1'
,
'height: 58px'
,
'border: 1px solid #ddd'
,
'border-radius: 29px'
,
'background: #fff'
,
'color: #666'
,
'font-size: 24px'
,
].
join
(
';'
);
confirmCancelElement
.
onclick
=
()
=>
closeConfirm
(
false
);
confirmOkElement
=
document
.
createElement
(
'button'
);
confirmOkElement
.
type
=
'button'
;
confirmOkElement
.
style
.
cssText
=
[
'flex: 1'
,
'height: 58px'
,
'border: 0'
,
'border-radius: 29px'
,
'background: #AA0000'
,
'color: #fff'
,
'font-size: 24px'
,
].
join
(
';'
);
confirmOkElement
.
onclick
=
()
=>
closeConfirm
(
true
);
confirmMaskElement
.
onclick
=
(
event
)
=>
{
if
(
event
.
target
===
confirmMaskElement
)
{
closeConfirm
(
false
);
}
};
buttonWrap
.
append
(
confirmCancelElement
,
confirmOkElement
);
confirmBoxElement
.
append
(
confirmTitleElement
,
confirmMessageElement
,
buttonWrap
);
confirmMaskElement
.
appendChild
(
confirmBoxElement
);
document
.
body
.
appendChild
(
confirmMaskElement
);
}
export
function
$confirm
(
message
?:
string
,
title
?:
string
):
Promise
<
boolean
>
;
export
function
$confirm
(
options
?:
ConfirmOptions
):
Promise
<
boolean
>
;
export
function
$confirm
(
messageOrOptions
?:
string
|
ConfirmOptions
,
title
?:
string
):
Promise
<
boolean
>
{
const
options
:
ConfirmOptions
=
typeof
messageOrOptions
===
'object'
?
messageOrOptions
:
{
message
:
messageOrOptions
,
title
};
if
(
!
options
.
message
)
{
return
Promise
.
resolve
(
false
);
}
ensureConfirmElement
();
if
(
confirmResolve
)
{
confirmResolve
(
false
);
confirmResolve
=
undefined
;
}
if
(
confirmHideTimer
)
{
window
.
clearTimeout
(
confirmHideTimer
);
confirmHideTimer
=
undefined
;
}
confirmTitleElement
!
.
textContent
=
options
.
title
??
'提示'
;
confirmMessageElement
!
.
textContent
=
options
.
message
;
confirmCancelElement
!
.
textContent
=
options
.
cancelText
??
'取消'
;
confirmOkElement
!
.
textContent
=
options
.
confirmText
??
'确定'
;
confirmMaskElement
!
.
style
.
display
=
'flex'
;
window
.
requestAnimationFrame
(()
=>
{
if
(
!
confirmMaskElement
||
!
confirmBoxElement
)
{
return
;
}
confirmMaskElement
.
style
.
opacity
=
'1'
;
confirmBoxElement
.
style
.
transform
=
'scale(1)'
;
});
return
new
Promise
<
boolean
>
((
resolve
)
=>
{
confirmResolve
=
resolve
;
});
}
\ No newline at end of file
frontend-large/src/commons/ws.ts
View file @
40a1a7b0
...
...
@@ -57,7 +57,7 @@ export function initSocket(args: SocketInitArgs): Socket | null {
socket
.
onAny
((
evt
,
payload
)
=>
{
const
data
=
decode
(
payload
);
if
(
$is_run_local
())
{
console
.
log
(
"
recv<============
"
,
evt
,
data
);
console
.
log
(
"
============>recv
"
,
evt
,
data
);
}
messageHandlers
.
forEach
((
handler
)
=>
handler
(
evt
,
data
));
});
...
...
@@ -78,7 +78,7 @@ export function sendSocketMessage(cmd: string, data: any = {}): boolean {
return
false
;
}
if
(
$is_run_local
())
{
console
.
log
(
'
================>send
'
,
cmd
,
data
);
console
.
log
(
'
send<================
'
,
cmd
,
data
);
}
socket
.
send
(
encode
({
cmd
,
data
}));
return
true
;
...
...
frontend-large/src/composables/useAdminGameSocket.ts
View file @
40a1a7b0
import
CryptoJS
from
'crypto-js'
import
{
onBeforeUnmount
,
onMounted
}
from
'vue'
import
{
$read_socket_storge
,
$remove_socket_storage
}
from
'@/commons/utils'
import
{
$read_socket_storge
,
$remove_socket_storage
,
$toast
}
from
'@/commons/utils'
import
{
initSocket
,
onSocketMessage
,
sendSocketMessage
}
from
'@/commons/ws'
import
router
from
'@/router'
...
...
@@ -64,21 +64,18 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
offSocketMessage
=
onSocketMessage
(
async
(
evt
,
payload
:
SocketPayload
)
=>
{
const
{
msg
,
state
,
data
}
=
payload
if
(
state
===
0
)
{
// if (evt === 'game_start_result' && data === 1) {
// return
// }
if
(
evt
==
'login_result'
)
{
alert
(
msg
)
//
alert(msg)
// options.onRelogin?.()
socket
?.
close
(
);
$toast
(
msg
);
$remove_socket_storage
();
await
router
.
replace
(
`/login`
);
location
.
href
=
location
.
href
;
return
;
}
else
if
(
evt
==
'new_admin_result'
)
{
alert
(
msg
);
socket
?.
close
(
);
//
alert(msg);
$toast
(
msg
);
$remove_socket_storage
();
await
router
.
replace
(
`/login`
);
location
.
href
=
location
.
href
;
...
...
frontend-large/src/pages/Login.vue
View file @
40a1a7b0
<
script
setup
lang=
"ts"
>
import
{
cssAssetUrl
}
from
'@/commons/assets'
;
import
{
$save
}
from
'@/commons/utils.ts'
;
import
{
$save
,
$toast
}
from
'@/commons/utils.ts'
;
import
{
onMounted
,
ref
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
...
...
@@ -25,10 +25,11 @@ async function login() {
})
const
data
=
await
res
.
json
()
// if (!res.ok || data.state !== 1) {
// alert(data.msg || 'Login failed')
if
(
!
res
.
ok
||
data
.
state
!==
1
)
{
$toast
(
data
.
msg
)
//alert(data.msg || 'Login failed')
// throw new Error(data.msg || 'Login failed')
//
}
}
return
data
}
...
...
@@ -36,13 +37,15 @@ const loginHandler = async () => {
const
{
state
,
msg
,
data
}
=
await
login
()
if
(
state
===
0
)
{
alert
(
msg
)
// alert(msg)
$toast
(
msg
)
return
}
for
(
let
i
in
data
)
{
$save
(
i
,
data
[
i
]);
}
alert
(
'登录成功'
)
// alert('登录成功')
$toast
(
'登录成功'
)
await
router
.
replace
(
`/main`
);
}
...
...
@@ -77,7 +80,6 @@ onMounted(() => {
</
template
>
<
style
scoped
>
.bg
{
width
:
1920px
;
height
:
1080px
;
...
...
frontend-large/src/pages/game1/Game.vue
View file @
40a1a7b0
...
...
@@ -7,7 +7,7 @@ import Rank2 from './views/Rank2View.vue'
import
{
useAdminGameSocket
}
from
'@/composables/useAdminGameSocket'
import
type
Player
from
'@/commons/player.ts'
import
{
playApplauseMusic
,
playGame1Music
,
stopAllMusic
,
stopGame1Music
}
from
'@/commons/music'
import
{
$
remove_socket_storage
}
from
'@/commons/utils.ts'
import
{
$
confirm
,
$remove_socket_storage
,
$toast
}
from
'@/commons/utils.ts'
const
router
=
useRouter
()
...
...
@@ -155,7 +155,8 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
const
startGameWithMusic
=
()
=>
{
if
(
playerCount
.
value
==
0
)
{
alert
(
'房间中没有玩家'
)
// alert('房间中没有玩家')
$toast
(
'房间中没有玩家'
);
return
;
}
startGame
()
...
...
@@ -171,10 +172,16 @@ const nextRoundWithMusic = () => {
nextRound
()
}
const
backHandler
=
()
=>
{
if
(
confirm
(
'是否关闭当前房间回到首页'
))
{
const
backHandler
=
async
()
=>
{
const
ok
=
await
$confirm
({
title
:
'提示'
,
message
:
'是否关闭当前房间回到首页'
,
confirmText
:
'确定'
,
cancelText
:
'取消'
,
})
if
(
ok
)
{
closeRoom
();
stop
Game1
Music
()
stop
All
Music
()
router
.
replace
(
'/main'
);
}
}
...
...
@@ -191,9 +198,9 @@ onUnmounted(() => {
watch
(
screen
,
(
value
)
=>
{
// console.log(value);
if
(
value
==
'rank2'
)
{
if
(
value
==
'rank2'
)
{
playApplauseMusic
();
}
else
if
(
value
==
'loading'
)
{
}
else
if
(
value
==
'loading'
)
{
playGame1Music
();
}
})
...
...
frontend-large/src/pages/game1/views/Rank2View.vue
View file @
40a1a7b0
...
...
@@ -152,11 +152,10 @@ const renderAvatar = (item: any | null) => {
.podium
{
background
:
v-bind
(
'imageUrls.podium'
)
no-repeat
center
;
width
:
1
347
px
;
height
:
269
px
;
width
:
1
920
px
;
height
:
740
px
;
position
:
absolute
;
top
:
427px
;
left
:
287px
;
bottom
:
0
;
.gu-burst
{
width
:
200px
;
...
...
@@ -174,6 +173,8 @@ const renderAvatar = (item: any | null) => {
.rank-no1
{
position
:
relative
;
left
:
308px
;
top
:
150px
;
.gu-burst
{
left
:
580px
;
...
...
@@ -207,7 +208,8 @@ const renderAvatar = (item: any | null) => {
.rank-no2
{
position
:
relative
;
left
:
308px
;
top
:
150px
;
.gu-burst
{
left
:
140px
;
top
:
-170px
;
...
...
@@ -232,7 +234,7 @@ const renderAvatar = (item: any | null) => {
text-align
:
center
;
line-height
:
46px
;
position
:
absolute
;
left
:
1
57
px
;
left
:
1
65
px
;
top
:
2px
;
z-index
:
1
;
border-radius
:
23px
;
...
...
@@ -241,7 +243,8 @@ const renderAvatar = (item: any | null) => {
.rank-no3
{
position
:
relative
;
left
:
308px
;
top
:
150px
;
.gu-burst
{
left
:
1020px
;
top
:
-170px
;
...
...
@@ -280,8 +283,8 @@ const renderAvatar = (item: any | null) => {
height
:
238px
;
background
:
v-bind
(
'imageUrls.vip'
);
position
:
absolute
;
left
:
171
px
;
top
:
-
20
0px
;
left
:
480
px
;
top
:
-
5
0px
;
}
.rank-container
{
...
...
@@ -324,19 +327,21 @@ const renderAvatar = (item: any | null) => {
position
:
absolute
;
inset
:
0
;
border-radius
:
inherit
;
background
:
v-bind
(
'imageUrls.avatarAnimation'
);
transform-origin
:
center
center
;
background
:
v-bind
(
'imageUrls.avatarAnimation'
)
center
/
cover
no-repeat
;
animation
:
avatar-rotate
10s
linear
infinite
;
}
.img-avatar
{
position
:
relative
;
top
:
6px
;
position
:
absolute
;
left
:
50%
;
top
:
50%
;
z-index
:
1
;
margin
:
0
auto
;
width
:
60px
;
height
:
60px
;
border-radius
:
30px
;
background
:
v-bind
(
'imageUrls.avatar'
);
transform
:
translate
(
-50%
,
-50%
);
background
:
v-bind
(
'imageUrls.avatar'
)
center
/
cover
no-repeat
;
}
.txt-nickname
{
...
...
frontend-large/src/pages/game4/Game4.vue
View file @
40a1a7b0
...
...
@@ -8,7 +8,7 @@ import { useAdminGameSocket } from "@/composables/useAdminGameSocket";
import
{
onSocketMessage
}
from
"@/commons/ws"
;
import
type
Player
from
"@/commons/player.ts"
;
import
{
useRouter
}
from
'vue-router'
import
{
$
remove_socket_storage
}
from
"@/commons/utils.ts"
;
import
{
$
confirm
,
$remove_socket_storage
,
$toast
}
from
"@/commons/utils.ts"
;
const
router
=
useRouter
()
...
...
@@ -194,7 +194,8 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
const
startGameWithMusic
=
()
=>
{
if
(
playerCount
.
value
==
0
)
{
alert
(
'房间中没有玩家'
)
// alert('房间中没有玩家')
$toast
(
'房间中没有玩家'
);
return
;
}
startGame
()
...
...
@@ -210,10 +211,16 @@ const nextRoundWithMusic = () => {
nextRound
()
}
const
backHandler
=
()
=>
{
if
(
confirm
(
'是否关闭当前房间回到首页'
))
{
const
backHandler
=
async
()
=>
{
const
ok
=
await
$confirm
({
title
:
'提示'
,
message
:
'是否关闭当前房间回到首页'
,
confirmText
:
'确定'
,
cancelText
:
'取消'
,
})
if
(
ok
)
{
closeRoom
();
stop
Game4
Music
()
stop
All
Music
()
router
.
replace
(
'/main'
);
}
}
...
...
frontend-large/src/pages/game5/Game5.vue
View file @
40a1a7b0
...
...
@@ -7,7 +7,7 @@ import Rank2 from './views/Rank2View.vue'
import
{
useAdminGameSocket
}
from
'@/composables/useAdminGameSocket'
import
type
Player
from
'@/commons/player.ts'
import
{
playApplauseMusic
,
playGame5Music
,
stopAllMusic
,
stopGame5Music
}
from
'@/commons/music'
import
{
$
remove_socket_storage
}
from
'@/commons/utils.ts'
import
{
$
confirm
,
$remove_socket_storage
,
$toast
}
from
'@/commons/utils.ts'
const
router
=
useRouter
()
...
...
@@ -163,7 +163,8 @@ const { startGame, createRoom, closeRoom, backRoom } = useAdminGameSocket({
const
startGameWithMusic
=
()
=>
{
if
(
playerCount
.
value
==
0
)
{
alert
(
'房间中没有玩家'
)
// alert('房间中没有玩家')
$toast
(
'房间中没有玩家'
);
return
;
}
playGame5Music
()
...
...
@@ -191,10 +192,16 @@ onUnmounted(() => {
window
.
removeEventListener
(
'beforeunload'
,
confirmRefresh
)
})
const
backHandler
=
()
=>
{
if
(
confirm
(
'是否关闭当前房间回到首页'
))
{
const
backHandler
=
async
()
=>
{
const
ok
=
await
$confirm
({
title
:
'提示'
,
message
:
'是否关闭当前房间回到首页'
,
confirmText
:
'确定'
,
cancelText
:
'取消'
,
})
if
(
ok
)
{
closeRoom
();
stop
Game5
Music
()
stop
All
Music
()
router
.
replace
(
'/main'
);
}
}
...
...
frontend-large/src/pages/game6/Game.vue
View file @
40a1a7b0
...
...
@@ -7,7 +7,7 @@ import Rank2 from './views/Rank2View.vue'
import
{
useAdminGameSocket
}
from
'@/composables/useAdminGameSocket'
import
type
Player
from
'@/commons/player.ts'
import
{
playApplauseMusic
,
playGame6Music
,
stopAllMusic
,
stopGame6Music
}
from
'@/commons/music'
import
{
$
remove_socket_storage
}
from
'@/commons/utils.ts'
import
{
$
confirm
,
$remove_socket_storage
,
$toast
}
from
'@/commons/utils.ts'
const
router
=
useRouter
()
...
...
@@ -153,7 +153,8 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
const
startGameWithMusic
=
()
=>
{
if
(
playerCount
.
value
==
0
)
{
alert
(
'房间中没有玩家'
)
// alert('房间中没有玩家')
$toast
(
'房间中没有玩家'
);
return
;
}
startGame
()
...
...
@@ -169,10 +170,16 @@ const nextRoundWithMusic = () => {
nextRound
()
}
const
backHandler
=
()
=>
{
if
(
confirm
(
'是否关闭当前房间回到首页'
))
{
const
backHandler
=
async
()
=>
{
const
ok
=
await
$confirm
({
title
:
'提示'
,
message
:
'是否关闭当前房间回到首页'
,
confirmText
:
'确定'
,
cancelText
:
'取消'
,
})
if
(
ok
)
{
closeRoom
();
stop
Game6
Music
()
stop
All
Music
()
router
.
replace
(
'/main'
);
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment