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
8a5923a5
Commit
8a5923a5
authored
Jun 02, 2026
by
陈冲
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
更新h5代码
parent
948fdad3
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
438 additions
and
479 deletions
+438
-479
Cargo.lock
backend/Cargo.lock
+26
-26
Cargo.toml
backend/Cargo.toml
+2
-2
game.sql
backend/deploy/game.sql
+4
-6
game_mysql.sql
backend/deploy/game_mysql.sql
+2
-4
game.rs
backend/src/db/models/game.rs
+7
-11
socket_io.rs
backend/src/protocols/socket_io.rs
+65
-53
utils.ts
frontend-h5/src/commons/utils.ts
+26
-32
ws.ts
frontend-h5/src/commons/ws.ts
+21
-1
MobileStage.vue
frontend-h5/src/components/MobileStage.vue
+4
-4
useGameSocket.ts
frontend-h5/src/composables/useGameSocket.ts
+87
-72
Login.vue
frontend-h5/src/pages/Login.vue
+0
-123
Game1.vue
frontend-h5/src/pages/game1/Game1.vue
+71
-35
LoadingView.vue
frontend-h5/src/pages/game1/views/LoadingView.vue
+16
-72
ScoreView.vue
frontend-h5/src/pages/game1/views/ScoreView.vue
+2
-2
index.ts
frontend-h5/src/router/index.ts
+8
-14
vite.config.ts
frontend-h5/vite.config.ts
+1
-1
game1.mp3
frontend-large/src/assets/images/game1.mp3
+0
-0
qrcode.png
frontend-large/src/assets/images/game1/qrcode.png
+0
-0
music.ts
frontend-large/src/commons/music.ts
+34
-0
utils.ts
frontend-large/src/commons/utils.ts
+12
-2
ws.ts
frontend-large/src/commons/ws.ts
+4
-2
useAdminGameSocket.ts
frontend-large/src/composables/useAdminGameSocket.ts
+4
-4
Login.vue
frontend-large/src/pages/Login.vue
+8
-2
Game1.vue
frontend-large/src/pages/game1/Game1.vue
+15
-3
LoadingView.vue
frontend-large/src/pages/game1/views/LoadingView.vue
+2
-2
Rank2View.vue
frontend-large/src/pages/game1/views/Rank2View.vue
+14
-4
vite.config.ts
frontend-large/vite.config.ts
+3
-2
No files found.
backend/Cargo.lock
View file @
8a5923a5
...
@@ -1031,6 +1031,32 @@ dependencies = [
...
@@ -1031,6 +1031,32 @@ dependencies = [
]
]
[[package]]
[[package]]
name = "hddpServer"
version = "0.1.0"
dependencies = [
"aes",
"async-trait",
"base64",
"bytes",
"cbc",
"chrono",
"dashmap",
"lazy_static",
"md-5",
"rand 0.10.0",
"rmp-serde",
"rust-embed",
"salvo",
"serde",
"serde_json",
"socketioxide",
"sqlx",
"tokio",
"tower",
"tower-http",
]
[[package]]
name = "headers"
name = "headers"
version = "0.4.1"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
source = "registry+https://github.com/rust-lang/crates.io-index"
...
@@ -1611,32 +1637,6 @@ dependencies = [
...
@@ -1611,32 +1637,6 @@ dependencies = [
]
]
[[package]]
[[package]]
name = "minigame"
version = "0.1.0"
dependencies = [
"aes",
"async-trait",
"base64",
"bytes",
"cbc",
"chrono",
"dashmap",
"lazy_static",
"md-5",
"rand 0.10.0",
"rmp-serde",
"rust-embed",
"salvo",
"serde",
"serde_json",
"socketioxide",
"sqlx",
"tokio",
"tower",
"tower-http",
]
[[package]]
name = "miniz_oxide"
name = "miniz_oxide"
version = "0.8.9"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
source = "registry+https://github.com/rust-lang/crates.io-index"
...
...
backend/Cargo.toml
View file @
8a5923a5
[package]
[package]
name
=
"
minigame
"
name
=
"
hddpServer
"
version
=
"0.1.0"
version
=
"0.1.0"
edition
=
"2024"
edition
=
"2024"
...
@@ -27,7 +27,7 @@ cbc = { version = "0.1.2", features = ["alloc"] }
...
@@ -27,7 +27,7 @@ cbc = { version = "0.1.2", features = ["alloc"] }
md5
=
{
version
=
"0.10.6"
,
package
=
"md-5"
}
md5
=
{
version
=
"0.10.6"
,
package
=
"md-5"
}
[[bin]]
[[bin]]
name
=
"
minigame
"
name
=
"
hddpServer
"
[target.x86_64-unknown-linux-musl]
[target.x86_64-unknown-linux-musl]
linker
=
"rust-lld"
linker
=
"rust-lld"
...
...
backend/deploy/game.sql
View file @
8a5923a5
drop
table
if
exists
tb_game
;
drop
table
if
exists
tb_game
;
create
table
tb_game
create
table
tb_game
(
(
event_date
date
,
-- 活动日期
wechat_id
text
,
-- 微信id
wechat_id
varchar
(
50
),
-- 微信id
nickname
varchar
(
50
),
-- 昵称
nickname
varchar
(
50
),
-- 昵称
avatar
varchar
(
200
),
-- 头像
avatar
text
,
-- 头像
round_num
int
NOT
NULL
CHECK
(
round_num
between
1
and
3
),
-- 比赛场次
game_rank
int
,
-- 排名
item_num
int
not
null
check
(
item_num
between
1
and
6
),
-- 游戏项目(1到6)
item_num
int
not
null
check
(
item_num
between
1
and
6
),
-- 游戏项目(1到6)
score
int
not
null
default
0
check
(
score
>=
0
),
-- 得分
score
int
not
null
default
0
check
(
score
>=
0
),
-- 得分
create_date
timestamptz
,
-- 创建时间
create_date
timestamptz
-- 创建时间
unique
(
event_date
,
wechat_id
,
round_num
,
item_num
)
);
);
SELECT
*
FROM
tb_game
SELECT
*
FROM
tb_game
...
...
backend/deploy/game_mysql.sql
View file @
8a5923a5
DROP
TABLE
IF
EXISTS
tb_game
;
DROP
TABLE
IF
EXISTS
tb_game
;
CREATE
TABLE
tb_game
CREATE
TABLE
tb_game
(
(
event_date
DATE
,
wechat_id
VARCHAR
(
50
),
wechat_id
VARCHAR
(
50
),
nickname
VARCHAR
(
50
),
nickname
VARCHAR
(
50
),
avatar
VARCHAR
(
200
),
avatar
VARCHAR
(
200
),
round_num
INT
NOT
NULL
,
game_rank
INT
NOT
NULL
,
item_num
INT
NOT
NULL
,
item_num
INT
NOT
NULL
,
score
INT
NOT
NULL
DEFAULT
0
,
score
INT
NOT
NULL
DEFAULT
0
,
create_date
DATETIME
,
create_date
DATETIME
UNIQUE
KEY
uk_tb_game_player_item
(
event_date
,
wechat_id
,
round_num
,
item_num
)
);
);
DROP
TABLE
IF
EXISTS
tb_log
;
DROP
TABLE
IF
EXISTS
tb_log
;
...
...
backend/src/db/models/game.rs
View file @
8a5923a5
...
@@ -4,15 +4,13 @@ use serde_json::json;
...
@@ -4,15 +4,13 @@ use serde_json::json;
use
crate
::
db
::
models
::
log
::
Log
;
use
crate
::
db
::
models
::
log
::
Log
;
pub
struct
Game
{
pub
struct
Game
{
/// 活动日期
pub
event_date
:
NaiveDate
,
pub
wechat_id
:
String
,
pub
wechat_id
:
String
,
pub
nickname
:
Option
<
String
>
,
pub
nickname
:
String
,
pub
avatar
:
Option
<
String
>
,
pub
avatar
:
String
,
/// 比赛场次
pub
round_num
:
i32
,
/// 游戏项目(1到6)
/// 游戏项目(1到6)
pub
item_num
:
i32
,
pub
item_num
:
i32
,
/// 排序
pub
game_rank
:
i32
,
/// 得分
/// 得分
pub
score
:
i32
,
pub
score
:
i32
,
/// 创建时间
/// 创建时间
...
@@ -22,14 +20,13 @@ pub struct Game {
...
@@ -22,14 +20,13 @@ pub struct Game {
impl
Game
{
impl
Game
{
pub
async
fn
insert
(
&
self
)
->
bool
{
pub
async
fn
insert
(
&
self
)
->
bool
{
let
res
=
crate
::
db
::
db_query
(
let
res
=
crate
::
db
::
db_query
(
r
#
"insert into tb_game(
event_date,wechat_id,nickname,avatar,round_num,item_num,score,create_date) values(?::date,
?,?,?,?,?,?,now());"
#
,
r
#
"insert into tb_game(
wechat_id,nickname,avatar,item_num,game_rank,score,create_date) values(
?,?,?,?,?,?,now());"
#
,
vec!
[
vec!
[
json!
(
&
self
.event_date
),
json!
(
&
self
.wechat_id
),
json!
(
&
self
.wechat_id
),
json!
(
&
self
.nickname
),
json!
(
&
self
.nickname
),
json!
(
&
self
.avatar
),
json!
(
&
self
.avatar
),
json!
(
&
self
.round_num
),
json!
(
&
self
.item_num
),
json!
(
&
self
.item_num
),
json!
(
&
self
.game_rank
),
json!
(
&
self
.score
),
json!
(
&
self
.score
),
],
],
)
)
...
@@ -46,12 +43,11 @@ impl Game {
...
@@ -46,12 +43,11 @@ impl Game {
id
:
0
,
id
:
0
,
msg
:
err
,
msg
:
err
,
params
:
json!
({
params
:
json!
({
"event_date"
:
self
.event_date
,
"wechat_id"
:
self
.wechat_id
,
"wechat_id"
:
self
.wechat_id
,
"nickname"
:
self
.nickname
,
"nickname"
:
self
.nickname
,
"avatar"
:
self
.avatar
,
"avatar"
:
self
.avatar
,
"round_num"
:
self
.round_num
,
"item_num"
:
self
.item_num
,
"item_num"
:
self
.item_num
,
"game_rank"
:
self
.game_rank
,
"score"
:
self
.score
,
"score"
:
self
.score
,
}),
}),
create_date
:
None
,
create_date
:
None
,
...
...
backend/src/protocols/socket_io.rs
View file @
8a5923a5
use
crate
::
commons
::
utils
::{
self
,
CONFIG
as
config
};
use
crate
::
commons
::
utils
::{
self
,
CONFIG
as
config
};
use
crate
::
db
::
models
::
game
::
Game
;
use
crate
::
db
::
models
::
manager
::
*
;
use
crate
::
db
::
models
::
manager
::
*
;
use
bytes
::
Bytes
;
use
bytes
::
Bytes
;
use
dashmap
::
DashMap
;
use
dashmap
::
DashMap
;
...
@@ -8,6 +9,7 @@ use serde_json::{Value, json};
...
@@ -8,6 +9,7 @@ use serde_json::{Value, json};
use
socketioxide
::
SocketIo
;
use
socketioxide
::
SocketIo
;
use
socketioxide
::
extract
::{
Data
,
SocketRef
};
use
socketioxide
::
extract
::{
Data
,
SocketRef
};
use
socketioxide
::
socket
::
DisconnectReason
;
use
socketioxide
::
socket
::
DisconnectReason
;
use
sqlx
::
encode
::
IsNull
::
No
;
use
std
::
collections
::
HashMap
;
use
std
::
collections
::
HashMap
;
use
std
::
sync
::
atomic
::{
AtomicU64
,
Ordering
};
use
std
::
sync
::
atomic
::{
AtomicU64
,
Ordering
};
use
std
::
time
::
Duration
;
use
std
::
time
::
Duration
;
...
@@ -24,31 +26,28 @@ static GAME_ROUND_ID: AtomicU64 = AtomicU64::new(0);
...
@@ -24,31 +26,28 @@ static GAME_ROUND_ID: AtomicU64 = AtomicU64::new(0);
const
ROOM_ENTRY_LIMIT
:
usize
=
20
;
const
ROOM_ENTRY_LIMIT
:
usize
=
20
;
///游戏结束排序最大用户数
///游戏结束排序最大用户数
const
ROOM_SCORE_RANK_LIMIT
:
usize
=
10
;
const
ROOM_SCORE_RANK_LIMIT
:
usize
=
10
;
///每局游戏时长
const
GAME_DURATION_SECS
:
u64
=
60
;
#[derive(Clone,
Default)]
#[derive(Clone,
Default)]
struct
RoomPlayer
{
struct
RoomPlayer
{
/// 成绩分数,具体含义由前端和游戏逻辑自行定义,后端只负责存储和排序。
/// 成绩分数,具体含义由前端和游戏逻辑自行定义,后端只负责存储和排序。
score
:
i64
,
score
:
i64
,
// /// wechat原始token用于记录
// wechat: String,
/// 是否在线,离线的玩家不允许提交分数,但保留在排行榜里。
/// 是否在线,离线的玩家不允许提交分数,但保留在排行榜里。
online
:
bool
,
online
:
bool
,
/// 进入房间的顺序,用于游戏开始前展示前 20 个入房玩家。
/// 进入房间的顺序,用于游戏开始前展示前 20 个入房玩家。
entry_order
:
usize
,
entry_order
:
usize
,
/// 玩家昵称
/// 玩家昵称
nickname
:
String
,
nickname
:
String
,
/// 玩家联系方式
telephone
:
String
,
/// 头像
/// 头像
avatar
:
String
,
avatar
:
String
,
}
}
#[derive(Clone)]
#[derive(Clone)]
struct
RankingPlayer
{
struct
RankingPlayer
{
userid
:
String
,
wechat
:
String
,
score
:
i64
,
score
:
i64
,
nickname
:
String
,
nickname
:
String
,
telephone
:
String
,
avatar
:
String
,
avatar
:
String
,
}
}
...
@@ -76,18 +75,17 @@ impl RoomState {
...
@@ -76,18 +75,17 @@ impl RoomState {
})
})
}
}
fn
ranking_from_player
(
userid
:
&
str
,
player
:
&
RoomPlayer
)
->
RankingPlayer
{
fn
ranking_from_player
(
wechat
:
&
str
,
player
:
&
RoomPlayer
)
->
RankingPlayer
{
RankingPlayer
{
RankingPlayer
{
userid
:
userid
.to_string
(),
wechat
:
wechat
.to_string
(),
score
:
player
.score
,
score
:
player
.score
,
nickname
:
player
.nickname
.clone
(),
nickname
:
player
.nickname
.clone
(),
telephone
:
player
.telephone
.clone
(),
avatar
:
player
.avatar
.clone
(),
avatar
:
player
.avatar
.clone
(),
}
}
}
}
fn
sort_score_ranking
(
ranking
:
&
mut
Vec
<
RankingPlayer
>
)
{
fn
sort_score_ranking
(
ranking
:
&
mut
Vec
<
RankingPlayer
>
)
{
ranking
.sort_by
(|
a
,
b
|
b
.score
.cmp
(
&
a
.score
)
.then_with
(||
a
.
userid
.cmp
(
&
b
.userid
)));
ranking
.sort_by
(|
a
,
b
|
b
.score
.cmp
(
&
a
.score
)
.then_with
(||
a
.
wechat
.cmp
(
&
b
.wechat
)));
}
}
fn
ranking_payload
(
ranking
:
&
[
RankingPlayer
])
->
Vec
<
Value
>
{
fn
ranking_payload
(
ranking
:
&
[
RankingPlayer
])
->
Vec
<
Value
>
{
...
@@ -97,10 +95,9 @@ impl RoomState {
...
@@ -97,10 +95,9 @@ impl RoomState {
.map
(|(
index
,
player
)|
{
.map
(|(
index
,
player
)|
{
json!
({
json!
({
"rank"
:
index
+
1
,
"rank"
:
index
+
1
,
"userid"
:
player
.userid
,
// "wechat": player.wechat
,
"score"
:
player
.score
,
"score"
:
player
.score
,
"nickname"
:
player
.nickname
,
"nickname"
:
player
.nickname
,
"telephone"
:
player
.telephone
,
"avatar"
:
player
.avatar
"avatar"
:
player
.avatar
})
})
})
})
...
@@ -133,12 +130,12 @@ impl RoomState {
...
@@ -133,12 +130,12 @@ impl RoomState {
ranking
ranking
}
}
fn
score_result_payload
(
&
self
,
userid
:
&
str
)
->
Option
<
Value
>
{
fn
score_result_payload
(
&
self
,
wechat
:
&
str
)
->
Option
<
Value
>
{
let
ranking
=
self
.full_score_ranking
();
let
ranking
=
self
.full_score_ranking
();
ranking
ranking
.iter
()
.iter
()
.position
(|
player
|
player
.
userid
==
userid
)
.position
(|
player
|
player
.
wechat
==
wechat
)
.map
(|
index
|
json!
(
index
+
1
))
.map
(|
index
|
json!
(
index
+
1
))
}
}
...
@@ -290,20 +287,18 @@ fn broadcast_room_rank(ranking: Vec<Value>) {
...
@@ -290,20 +287,18 @@ fn broadcast_room_rank(ranking: Vec<Value>) {
broadcast_msgpack_all_admin
(
"room_rank_result"
,
&
ok_resp
(
Some
(
json!
(
ranking
))));
broadcast_msgpack_all_admin
(
"room_rank_result"
,
&
ok_resp
(
Some
(
json!
(
ranking
))));
}
}
fn
spawn_game_timeout
(
round_id
:
u64
)
{
fn
spawn_game_timeout
(
round_id
:
u64
,
secs
:
u64
)
{
tokio
::
spawn
(
async
move
{
tokio
::
spawn
(
async
move
{
tokio
::
time
::
sleep
(
Duration
::
from_secs
(
GAME_DURATION_SECS
))
.await
;
tokio
::
time
::
sleep
(
Duration
::
from_secs
(
secs
))
.await
;
if
GAME_ROUND_ID
.load
(
Ordering
::
SeqCst
)
!=
round_id
{
if
GAME_ROUND_ID
.load
(
Ordering
::
SeqCst
)
!=
round_id
{
return
;
return
;
}
}
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
if
state
.status
!=
RoomStatus
::
Running
{
state
.status
=
RoomStatus
::
Submit
;
return
;
dbg!
(
"submit"
);
}
drop
(
state
);
state
.status
=
RoomStatus
::
Ended
;
}
}
});
});
}
}
...
@@ -354,16 +349,16 @@ async fn handle_room_command(
...
@@ -354,16 +349,16 @@ async fn handle_room_command(
if
is_admin
{
if
is_admin
{
return
;
return
;
}
}
//超过300人的情况
if
get_player_count
()
>
300
{
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"本场游戏人员已满"
,
Some
(
json!
(
3
))));
return
;
}
let
nickname
=
data
let
nickname
=
data
.get
(
"nickname"
)
.get
(
"nickname"
)
.and_then
(|
value
|
value
.as_str
())
.and_then
(|
value
|
value
.as_str
())
.unwrap_or
(
""
)
.unwrap_or
(
""
)
.to_string
();
.to_string
();
let
telephone
=
data
.get
(
"telephone"
)
.and_then
(|
value
|
value
.as_str
())
.unwrap_or
(
""
)
.to_string
();
let
avatar
=
data
let
avatar
=
data
.get
(
"avatar"
)
.get
(
"avatar"
)
.and_then
(|
value
|
value
.as_str
())
.and_then
(|
value
|
value
.as_str
())
...
@@ -397,7 +392,6 @@ async fn handle_room_command(
...
@@ -397,7 +392,6 @@ async fn handle_room_command(
.and_modify
(|
player
|
{
.and_modify
(|
player
|
{
player
.online
=
true
;
player
.online
=
true
;
player
.nickname
=
nickname
.to_string
();
player
.nickname
=
nickname
.to_string
();
player
.telephone
=
telephone
.to_string
();
player
.avatar
=
avatar
.to_string
();
player
.avatar
=
avatar
.to_string
();
})
})
.or_insert
(
RoomPlayer
{
.or_insert
(
RoomPlayer
{
...
@@ -405,7 +399,6 @@ async fn handle_room_command(
...
@@ -405,7 +399,6 @@ async fn handle_room_command(
online
:
true
,
online
:
true
,
entry_order
,
entry_order
,
nickname
:
nickname
.to_string
(),
nickname
:
nickname
.to_string
(),
telephone
:
telephone
.to_string
(),
avatar
:
avatar
.to_string
(),
avatar
:
avatar
.to_string
(),
});
});
let
count
=
state
.players
.len
();
let
count
=
state
.players
.len
();
...
@@ -445,43 +438,27 @@ async fn handle_room_command(
...
@@ -445,43 +438,27 @@ async fn handle_room_command(
let
round_id
=
GAME_ROUND_ID
.fetch_add
(
1
,
Ordering
::
SeqCst
)
+
1
;
let
round_id
=
GAME_ROUND_ID
.fetch_add
(
1
,
Ordering
::
SeqCst
)
+
1
;
// let snapshot = state.snapshot();
// let snapshot = state.snapshot();
drop
(
state
);
drop
(
state
);
// spawn_game_timeout(round_id);
//通知所有玩家倒计时60秒
//通知所有玩家倒计时60秒,实际上还有3秒倒计时,所以是63
spawn_game_timeout
(
round_id
,
63
);
broadcast_msgpack_all_player
(
evt
,
&
ok_resp
(
Some
(
json!
(
game_index
))));
broadcast_msgpack_all_player
(
evt
,
&
ok_resp
(
Some
(
json!
(
game_index
))));
//通知管理员
//通知管理员
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
Some
(
json!
(
game_index
))));
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
Some
(
json!
(
game_index
))));
}
}
}
}
// // 接收到服务端消息后给所有玩家通知提交分数
// submit_score玩家提交分数
// "notify_submit" => {
// submit_score_save则为60秒后提交的数据
// if !is_admin {
// return;
// }
// if let Ok(mut state) = ROOM_STATE.lock() {
// // state.reset();
// state.status = RoomStatus::Submit;
// drop(state);
// }
// emit_msgpack(&socket, evt, &ok_resp(None));
// broadcast_msgpack_all_player(evt, &json!({}));
// }
// 玩家提交分数
"submit_score"
=>
{
"submit_score"
=>
{
if
is_admin
{
if
is_admin
{
return
;
return
;
}
}
let
score
=
data
let
score
=
data
.get
(
"score"
)
.and_then
(|
v
|
v
.as_i64
())
.unwrap_or
(
0
);
.as_i64
()
.or_else
(||
data
.get
(
"score"
)
.and_then
(|
v
|
v
.as_i64
()))
.unwrap_or
(
0
);
// let _time = data.get("time").and_then(|v| v.as_i64()).unwrap_or(0);
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
if
!
state
.players
.contains_key
(
userid
)
{
if
!
state
.players
.contains_key
(
userid
)
{
drop
(
state
);
drop
(
state
);
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"您不在房间中"
,
None
));
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"您不在房间中"
,
None
));
}
else
if
!
matches!
(
state
.status
,
RoomStatus
::
Running
|
RoomStatus
::
Ended
)
{
}
else
if
!
matches!
(
state
.status
,
RoomStatus
::
Running
)
{
drop
(
state
);
drop
(
state
);
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"游戏还未开始"
,
None
));
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"游戏还未开始"
,
None
));
}
}
...
@@ -497,6 +474,7 @@ async fn handle_room_command(
...
@@ -497,6 +474,7 @@ async fn handle_room_command(
else
if
state
.players
.contains_key
(
userid
)
{
else
if
state
.players
.contains_key
(
userid
)
{
let
player
=
state
.players
.get_mut
(
userid
)
.unwrap
();
let
player
=
state
.players
.get_mut
(
userid
)
.unwrap
();
player
.score
=
score
;
player
.score
=
score
;
// player.wechat = wechat.to_string();
player
.online
=
true
;
player
.online
=
true
;
// 玩家分数排序
// 玩家分数排序
...
@@ -504,8 +482,6 @@ async fn handle_room_command(
...
@@ -504,8 +482,6 @@ async fn handle_room_command(
let
player_rank
=
state
.score_result_payload
(
userid
);
let
player_rank
=
state
.score_result_payload
(
userid
);
drop
(
state
);
drop
(
state
);
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
player_rank
));
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
player_rank
));
// 将数据提交到数据库
//通知管理员
//通知管理员
broadcast_room_rank
(
ranking
);
broadcast_room_rank
(
ranking
);
}
else
{
}
else
{
...
@@ -514,6 +490,42 @@ async fn handle_room_command(
...
@@ -514,6 +490,42 @@ async fn handle_room_command(
}
}
}
}
}
}
// 将数据提交到数据库,不用通知管理员
"submit_score_save"
=>
{
let
score
=
data
.get
(
"score"
)
.and_then
(|
v
|
v
.as_i64
())
.unwrap_or
(
0
);
if
let
(
Some
(
item_num
),
Some
(
wechat
),
Some
(
nickname
),
Some
(
avatar
),
Some
(
rank
))
=
(
data
.get
(
"item_num"
)
.and_then
(|
v
|
v
.as_i64
()),
data
.get
(
"wechat"
)
.and_then
(|
v
|
v
.as_str
()),
// 需要原始token
data
.get
(
"nickname"
)
.and_then
(|
v
|
v
.as_str
()),
data
.get
(
"avatar"
)
.and_then
(|
v
|
v
.as_str
()),
data
.get
(
"rank"
)
.and_then
(|
v
|
v
.as_i64
()),
)
{
if
wechat
.is_empty
()
{
return
;
}
if
let
Ok
(
state
)
=
ROOM_STATE
.lock
()
{
if
!
matches!
(
state
.status
,
RoomStatus
::
Submit
)
{
drop
(
state
);
return
;
}
drop
(
state
);
}
let
game
=
Game
{
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
,
};
if
game
.insert
()
.await
{
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
None
));
}
else
{
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"提交异常"
,
None
));
}
}
}
// 关闭房间
// 关闭房间
"room_close"
=>
{
"room_close"
=>
{
if
!
is_admin
{
if
!
is_admin
{
...
...
frontend-h5/src/commons/utils.ts
View file @
8a5923a5
import
SHA256
from
'crypto-js/sha256'
type
StorageRecord
=
Record
<
string
,
unknown
>
;
type
StorageRecord
=
Record
<
string
,
unknown
>
;
function
isStorageRecord
(
value
:
unknown
):
value
is
StorageRecord
{
function
isStorageRecord
(
value
:
unknown
):
value
is
StorageRecord
{
...
@@ -54,37 +56,6 @@ export function $read<T>(key: string | StorageRecord, defaultValue?: T): T | Sto
...
@@ -54,37 +56,6 @@ export function $read<T>(key: string | StorageRecord, defaultValue?: T): T | Sto
}
}
}
}
export
function
$goto_login
(
evt
:
string
,
router
:
any
,
path
:
string
):
boolean
{
if
(
evt
==
"login_result"
)
{
$remove_socket_storage
();
router
.
replace
(
path
).
then
(()
=>
{
});
return
true
;
}
return
false
;
}
export
function
$remove_socket_storage
():
void
{
localStorage
.
removeItem
(
'socket_path'
);
localStorage
.
removeItem
(
'client_ns'
);
localStorage
.
removeItem
(
'wechat_id'
);
localStorage
.
removeItem
(
'nickname'
);
localStorage
.
removeItem
(
'telephone'
);
localStorage
.
removeItem
(
'avatar'
);
}
export
function
$read_socket_storge
():
any
{
const
domain
=
$read
(
'domain'
,
''
);
const
socket_path
=
$read
(
'socket_path'
,
''
);
const
client_ns
=
$read
(
'client_ns'
,
''
)
const
wechat_id
=
$read
(
'wechat_id'
,
''
);
const
nickname
=
$read
(
'nickname'
,
''
);
const
telephone
=
$read
(
'telephone'
,
''
);
const
avatar
=
$read
(
'avatar'
,
''
);
return
{
domain
,
socket_path
,
client_ns
,
wechat_id
,
nickname
,
telephone
,
avatar
}
}
export
function
$format_str
(
str
:
string
,
num
:
number
)
{
export
function
$format_str
(
str
:
string
,
num
:
number
)
{
if
(
str
.
length
<=
num
-
2
)
{
if
(
str
.
length
<=
num
-
2
)
{
return
str
;
return
str
;
...
@@ -93,8 +64,30 @@ export function $format_str(str: string, num: number) {
...
@@ -93,8 +64,30 @@ export function $format_str(str: string, num: number) {
}
}
export
function
$is_run_local
():
boolean
{
export
function
$is_run_local
():
boolean
{
if
(
/localhost|192.168/gim
.
test
(
location
.
href
))
{
if
(
/
https
?
:
\/\/
localhost|192.168/gim
.
test
(
location
.
href
))
{
return
true
;
return
true
;
}
}
return
false
;
return
false
;
}
}
function
$query
<
T
extends
string
|
string
[]
>
(
q
:
T
):
T
{
const
query
=
location
.
href
.
match
(
/
\?([^
#
]
*
)
/
)?.[
1
]?.
replace
(
/
\/
$/
,
''
);
const
params
=
new
URLSearchParams
(
query
);
if
(
Array
.
isArray
(
q
))
{
return
q
.
map
((
key
)
=>
params
.
get
(
key
)).
filter
((
v
):
v
is
string
=>
!!
v
)
as
T
;
}
return
params
.
get
(
q
)
as
T
;
}
export
function
$getWechat
():
any
|
null
{
const
q
=
$query
([
'token'
,
'nickname'
,
'avatar'
]);
if
(
q
&&
q
.
length
==
3
)
{
return
{
token
:
SHA256
(
q
[
0
]).
toString
(),
//原token很长,这里压缩下,压缩后不能还原,最后提交成绩时要提交原始token
token_origin
:
q
[
0
],
nickname
:
q
[
1
],
avatar
:
q
[
2
],
};
}
return
null
;
}
\ No newline at end of file
frontend-h5/src/commons/ws.ts
View file @
8a5923a5
...
@@ -35,8 +35,19 @@ export function initSocket(args: SocketInitArgs): Socket | null {
...
@@ -35,8 +35,19 @@ export function initSocket(args: SocketInitArgs): Socket | null {
if
(
socket
)
{
if
(
socket
)
{
if
(
socket
.
connected
)
{
if
(
socket
.
connected
)
{
args
.
onConnect
?.(
socket
);
args
.
onConnect
?.(
socket
);
return
socket
;
}
}
return
socket
;
socket
.
removeAllListeners
();
socket
.
disconnect
();
socket
=
null
;
}
if
(
$is_run_local
())
{
console
.
log
(
'socket.io init:'
,
{
namespace
:
args
.
client_ns
,
path
:
args
.
socket_path
,
auth
:
args
.
auth
,
});
}
}
socket
=
io
(
args
.
client_ns
,
{
socket
=
io
(
args
.
client_ns
,
{
...
@@ -49,6 +60,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
...
@@ -49,6 +60,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
});
});
socket
.
on
(
'connect'
,
()
=>
{
socket
.
on
(
'connect'
,
()
=>
{
if
(
$is_run_local
())
{
console
.
log
(
'socket.io connected:'
,
socket
?.
id
);
}
args
.
onConnect
?.(
socket
!
);
args
.
onConnect
?.(
socket
!
);
});
});
socket
.
onAny
((
evt
,
payload
)
=>
{
socket
.
onAny
((
evt
,
payload
)
=>
{
...
@@ -60,6 +74,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
...
@@ -60,6 +74,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
});
});
socket
.
on
(
'disconnect'
,
(
reason
)
=>
{
socket
.
on
(
'disconnect'
,
(
reason
)
=>
{
if
(
$is_run_local
())
{
console
.
log
(
'socket.io disconnect:'
,
reason
);
}
args
.
onDisconnect
?.(
reason
);
args
.
onDisconnect
?.(
reason
);
});
});
...
@@ -72,6 +89,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
...
@@ -72,6 +89,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
export
function
sendSocketMessage
(
cmd
:
string
,
data
:
any
=
{}):
boolean
{
export
function
sendSocketMessage
(
cmd
:
string
,
data
:
any
=
{}):
boolean
{
if
(
!
socket
?.
connected
)
{
if
(
!
socket
?.
connected
)
{
if
(
$is_run_local
())
{
console
.
log
(
'socket.io send blocked:'
,
cmd
,
data
);
}
return
false
;
return
false
;
}
}
...
...
frontend-h5/src/components/MobileStage.vue
View file @
8a5923a5
...
@@ -144,11 +144,11 @@ defineExpose({
...
@@ -144,11 +144,11 @@ defineExpose({
<main
class=
"mobile-stage-viewport"
>
<main
class=
"mobile-stage-viewport"
>
<div
class=
"mobile-stage-background"
:style=
"backgroundStyle"
/>
<div
class=
"mobile-stage-background"
:style=
"backgroundStyle"
/>
<div
class=
"mobile-stage"
:style=
"stageStyle"
>
<div
class=
"mobile-stage"
:style=
"stageStyle"
>
<div
v-if=
"countdownVisible"
class=
"countdown-modal"
>
<div
:key=
"countdownKey"
class=
"countdown-number"
>
{{
countdown
}}
</div>
</div>
<slot
/>
<slot
/>
</div>
</div>
<div
v-if=
"countdownVisible"
class=
"countdown-modal"
>
<div
:key=
"countdownKey"
class=
"countdown-number"
>
{{
countdown
}}
</div>
</div>
</main>
</main>
</
template
>
</
template
>
...
@@ -187,7 +187,7 @@ defineExpose({
...
@@ -187,7 +187,7 @@ defineExpose({
.countdown-number
{
.countdown-number
{
color
:
white
;
color
:
white
;
font-size
:
3
00pt
;
font-size
:
2
00pt
;
font-weight
:
800
;
font-weight
:
800
;
line-height
:
1
;
line-height
:
1
;
text-shadow
:
0
18px
38px
rgba
(
0
,
0
,
0
,
0.25
);
text-shadow
:
0
18px
38px
rgba
(
0
,
0
,
0
,
0.25
);
...
...
frontend-h5/src/composables/useGameSocket.ts
View file @
8a5923a5
import
{
onBeforeUnmount
,
onMounted
,
ref
}
from
'vue'
import
{
ref
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
{
useRouter
}
from
'vue-router'
import
type
{
Socket
}
from
'socket.io-client'
import
type
{
Socket
}
from
'socket.io-client'
import
{
$read_socket_storge
}
from
'@/commons/utils'
import
{
initSocket
,
onSocketMessage
,
sendSocketMessage
}
from
'@/commons/ws'
import
{
initSocket
,
onSocketMessage
,
sendSocketMessage
}
from
'@/commons/ws'
import
{
$read
}
from
'@/commons/utils'
type
SocketPayload
=
{
type
SocketPayload
=
{
msg
?:
string
msg
?:
string
...
@@ -11,9 +11,12 @@ type SocketPayload = {
...
@@ -11,9 +11,12 @@ type SocketPayload = {
}
}
type
GameSocketOptions
=
{
type
GameSocketOptions
=
{
gameId
:
string
gameId
:
string
,
auth
:
any
,
onConnect
?:
()
=>
void
,
onGameStart
?:
(
data
:
any
)
=>
void
onGameStart
?:
(
data
:
any
)
=>
void
onScoreSubmitted
?:
(
data
:
any
)
=>
void
onScoreSubmitted
?:
(
is_save
:
boolean
,
data
:
any
)
=>
void
onRescoreSubmitted
?:
(
recount
:
number
)
=>
void
onRoomClosed
?:
(
data
:
any
)
=>
void
onRoomClosed
?:
(
data
:
any
)
=>
void
onEvent
?:
(
evt
:
string
,
payload
:
SocketPayload
)
=>
void
onEvent
?:
(
evt
:
string
,
payload
:
SocketPayload
)
=>
void
}
}
...
@@ -36,94 +39,106 @@ function getGameHomePath(data: any) {
...
@@ -36,94 +39,106 @@ function getGameHomePath(data: any) {
return
`/game
${
gameIndex
}
`
return
`/game
${
gameIndex
}
`
}
}
export
function
joinSharedRoom
(
nickname
:
string
,
token
:
string
,
avatar
:
string
)
{
return
sendSocketMessage
(
'room_join'
,
{
nickname
:
nickname
,
token
,
avatar
})
}
export
function
sendGameMessage
(
cmd
:
string
,
data
:
any
=
{})
{
return
sendSocketMessage
(
cmd
,
data
)
}
export
const
userJoinStatus
=
ref
(
false
)
export
function
useGameSocket
(
options
:
GameSocketOptions
)
{
export
function
useGameSocket
(
options
:
GameSocketOptions
)
{
const
router
=
useRouter
()
const
router
=
useRouter
()
const
userJoinStatus
=
ref
(
false
)
let
socket
:
Socket
|
null
=
null
let
socket
:
Socket
|
null
=
null
let
offSocketMessage
:
(()
=>
void
)
|
undefined
let
offSocketMessage
:
(()
=>
void
)
|
undefined
const
domain
=
$read
(
'domain'
,
''
);
let
resubmitcount
=
0
;
socket
=
initSocket
({
socket_path
:
'/socket.io'
,
client_ns
:
`
${
domain
}
/ws`
,
auth
:
options
.
auth
,
onConnect
:
(
socket
)
=>
{
options
.
onConnect
?.();
},
onDisconnect
:
(
_reason
:
string
)
=>
{
},
onError
:
(
error
:
any
)
=>
{
console
.
error
(
'socket.io connect_error:'
,
error
)
},
})
function
joinSharedRoom
()
{
offSocketMessage
=
onSocketMessage
((
evt
,
payload
:
SocketPayload
)
=>
{
const
{
nickname
,
telephone
,
avatar
}
=
$read_socket_storge
()
const
{
msg
,
state
,
data
}
=
payload
return
sendSocketMessage
(
'room_join'
,
{
nickname
,
telephone
,
avatar
})
}
function
sendGameMessage
(
cmd
:
string
,
data
:
any
=
{})
{
return
sendSocketMessage
(
cmd
,
data
)
}
onMounted
(()
=>
{
if
(
state
===
0
)
{
const
{
domain
,
socket_path
,
client_ns
,
wechat_id
}
=
$read_socket_storge
()
if
(
evt
===
'room_join_result'
)
{
switch
(
data
)
{
socket
=
initSocket
({
case
1
:
userJoinStatus
.
value
=
true
;
break
;
socket_path
:
`
${
domain
}${
socket_path
}
`
,
case
3
:
alert
(
msg
);
break
;
client_ns
,
auth
:
{
userid
:
wechat_id
,
},
onConnect
:
()
=>
{
window
.
setTimeout
(
joinSharedRoom
,
200
)
},
onDisconnect
:
(
_reason
:
string
)
=>
{
},
onError
:
(
_error
:
any
)
=>
{
},
})
offSocketMessage
=
onSocketMessage
((
evt
,
payload
:
SocketPayload
)
=>
{
const
{
msg
,
state
,
data
}
=
payload
if
(
state
===
0
&&
evt
===
'room_join_result'
)
{
if
(
data
===
1
)
{
userJoinStatus
.
value
=
true
}
}
return
}
else
if
(
evt
==
'submit_score_save'
)
{
//提交分数的情况下有问题的情况下,只重试3次
if
(
resubmitcount
>=
3
)
{
return
;
}
setTimeout
(()
=>
{
resubmitcount
++
;
options
.
onRescoreSubmitted
?.(
resubmitcount
);
},
200
);
}
}
return
;
}
if
(
state
===
0
)
{
if
(
state
===
0
)
{
alert
(
msg
)
alert
(
msg
)
return
return
}
}
switch
(
evt
)
{
switch
(
evt
)
{
case
'room_create_result'
:
{
case
'room_create_result'
:
{
const
nextPath
=
getGameHomePath
(
data
)
const
nextPath
=
getGameHomePath
(
data
)
if
(
nextPath
&&
router
.
currentRoute
.
value
.
path
!==
nextPath
)
{
if
(
nextPath
&&
router
.
currentRoute
.
value
.
path
!==
nextPath
)
{
router
.
replace
(
nextPath
)
router
.
replace
(
nextPath
)
}
break
}
}
case
'room_join_result'
:
{
break
userJoinStatus
.
value
=
true
}
break
case
'room_join_result'
:
{
}
userJoinStatus
.
value
=
true
case
'game_start_result'
:
{
break
if
(
data
===
options
.
gameId
)
{
}
options
.
onGameStart
?.(
data
)
case
'game_start_result'
:
{
}
if
(
data
===
options
.
gameId
)
{
break
options
.
onGameStart
?.(
data
)
}
case
'submit_score_result'
:
{
options
.
onScoreSubmitted
?.(
data
)
break
}
case
'room_close'
:
{
userJoinStatus
.
value
=
false
options
.
onRoomClosed
?.(
data
)
break
}
}
break
}
}
case
'submit_score_result'
:
{
options
.
onScoreSubmitted
?.(
false
,
data
)
break
}
case
'submit_score_save_result'
:
{
options
.
onScoreSubmitted
?.(
true
,
data
)
break
}
case
'room_close'
:
{
userJoinStatus
.
value
=
false
options
.
onRoomClosed
?.(
data
)
break
}
}
options
.
onEvent
?.(
evt
,
payload
)
options
.
onEvent
?.(
evt
,
payload
)
})
})
})
onBeforeUnmount
(()
=>
{
//
onBeforeUnmount(() => {
offSocketMessage
?.()
//
offSocketMessage?.()
})
//
})
return
{
return
{
socket
,
socket
,
userJoinStatus
,
userJoinStatus
,
joinSharedRoom
,
offSocketMessage
sendGameMessage
,
}
}
}
}
frontend-h5/src/pages/Login.vue
deleted
100644 → 0
View file @
948fdad3
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
MobileStage
from
'@/components/MobileStage.vue'
import
{
$save
}
from
'@/commons/utils'
const
router
=
useRouter
()
const
nickname
=
ref
(
''
)
async
function
getConn
()
{
let
domain
=
localStorage
.
getItem
(
'domain'
);
const
res
=
await
fetch
(
`
${
domain
}
/manager/getConn`
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
},
body
:
JSON
.
stringify
({}),
})
const
data
=
await
res
.
json
()
if
(
!
res
.
ok
||
data
.
state
!==
1
)
{
alert
(
data
.
msg
||
'Login failed'
)
throw
new
Error
(
data
.
msg
||
'Login failed'
)
}
return
data
}
const
loginHandler
=
async
()
=>
{
const
{
state
,
msg
,
data
}
=
await
getConn
()
if
(
state
===
0
)
{
alert
(
msg
)
return
}
for
(
let
i
in
data
)
{
$save
(
i
,
data
[
i
]);
}
$save
({
wechat_id
:
nickname
.
value
,
nickname
:
nickname
.
value
,
telephone
:
'13111223344'
,
avatar
:
'13111223344'
,
});
await
router
.
replace
(
'/game1'
)
}
</
script
>
<
template
>
<MobileStage>
<section
class=
"h5-page login-page"
>
<div
class=
"brand"
>
<span
class=
"brand-mark"
>
H5
</span>
<div>
<h1
class=
"h5-title"
>
小游戏
</h1>
<p
class=
"h5-subtitle"
>
小屏互动端
</p>
</div>
</div>
<form
class=
"login-form h5-panel"
>
<label
class=
"field"
>
<span>
昵称
</span>
<input
v-model=
"nickname"
maxlength=
"16"
placeholder=
"请输入昵称"
autocomplete=
"nickname"
>
</label>
<input
class=
"h5-button"
@
click=
"loginHandler"
type=
"button"
value=
"进入游戏"
>
</form>
</section>
</MobileStage>
</
template
>
<
style
scoped
>
.login-page
{
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
gap
:
32px
;
}
.brand
{
display
:
flex
;
align-items
:
center
;
gap
:
14px
;
padding-top
:
18px
;
}
.brand-mark
{
display
:
grid
;
width
:
58px
;
height
:
58px
;
place-items
:
center
;
border-radius
:
8px
;
background
:
var
(
--color-primary
);
color
:
white
;
font-size
:
20px
;
font-weight
:
900
;
}
.login-form
{
display
:
grid
;
gap
:
20px
;
padding
:
18px
;
}
.field
{
display
:
grid
;
gap
:
8px
;
color
:
var
(
--color-muted
);
font-size
:
14px
;
}
.field
input
{
width
:
100%
;
height
:
48px
;
border
:
1px
solid
var
(
--color-border
);
border-radius
:
8px
;
padding
:
0
14px
;
background
:
#fff
;
color
:
var
(
--color-text
);
outline
:
none
;
}
.field
input
:focus
{
border-color
:
var
(
--color-primary
);
}
</
style
>
frontend-h5/src/pages/game1/Game1.vue
View file @
8a5923a5
...
@@ -2,10 +2,11 @@
...
@@ -2,10 +2,11 @@
import
{
onBeforeUnmount
,
onMounted
,
ref
}
from
'vue'
import
{
onBeforeUnmount
,
onMounted
,
ref
}
from
'vue'
import
{
cssAssetUrl
}
from
'@/commons/assets.ts'
import
{
cssAssetUrl
}
from
'@/commons/assets.ts'
import
MobileStage
from
'@/components/MobileStage.vue'
import
MobileStage
from
'@/components/MobileStage.vue'
import
{
useGameSocket
}
from
'@/composables/useGameSocket'
import
{
useGameSocket
,
joinSharedRoom
,
sendGameMessage
,
userJoinStatus
}
from
'@/composables/useGameSocket'
import
LoadingView
from
'./views/LoadingView.vue'
import
LoadingView
from
'./views/LoadingView.vue'
import
PlayingView
from
'./views/PlayingView.vue'
import
PlayingView
from
'./views/PlayingView.vue'
import
ScoreView
from
'./views/ScoreView.vue'
import
ScoreView
from
'./views/ScoreView.vue'
import
{
$getWechat
}
from
'@/commons/utils.ts'
type
GameView
=
'loading'
|
'playing'
|
'score'
type
GameView
=
'loading'
|
'playing'
|
'score'
...
@@ -25,16 +26,20 @@ const imageUrls = {
...
@@ -25,16 +26,20 @@ const imageUrls = {
replay
:
cssAssetUrl
(
'game1/replay.png'
),
replay
:
cssAssetUrl
(
'game1/replay.png'
),
}
}
const
wechat
=
$getWechat
()
const
currentView
=
ref
<
GameView
>
(
'loading'
)
const
currentView
=
ref
<
GameView
>
(
'loading'
)
const
countdownInterval
=
ref
(
60
)
const
countdownInterval
=
ref
(
60
)
const
isDivDescVisible
=
ref
(
false
)
const
isDivDescVisible
=
ref
(
false
)
const
tick
=
ref
(
0
)
const
tick
=
ref
(
0
)
//点击次数
const
rank
=
ref
(
8
)
const
rank
=
ref
(
0
)
//排名
const
level
=
ref
(
5
)
const
guFrames
:
[
string
,
string
,
string
]
=
[
imageUrls
.
gu01
,
imageUrls
.
gu02
,
imageUrls
.
gu03
]
const
guFrames
:
[
string
,
string
,
string
]
=
[
imageUrls
.
gu01
,
imageUrls
.
gu02
,
imageUrls
.
gu03
]
const
currentGu
=
ref
(
imageUrls
.
gu01
)
const
currentGu
=
ref
(
imageUrls
.
gu01
)
const
isGuPlaying
=
ref
(
false
)
const
isGuPlaying
=
ref
(
false
)
const
token
=
ref
(
wechat
?.
token
??
''
)
const
nickname
=
ref
(
wechat
?.
nickname
??
''
)
const
avatar
=
ref
(
wechat
?.
avatar
??
''
)
const
mobileStageRef
=
ref
<
InstanceType
<
typeof
MobileStage
>
|
null
>
(
null
)
const
mobileStageRef
=
ref
<
InstanceType
<
typeof
MobileStage
>
|
null
>
(
null
)
const
_offSocketMessage
=
ref
<
(()
=>
void
)
|
undefined
>
()
let
guFrameTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
let
guFrameTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
let
gameCountdownTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
let
gameCountdownTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
...
@@ -55,8 +60,22 @@ function stopGameCountdown() {
...
@@ -55,8 +60,22 @@ function stopGameCountdown() {
}
}
}
}
function
submitScore
()
{
function
submitScore
(
save
:
boolean
=
false
)
{
sendGameMessage
(
'submit_score'
,
{
score
:
tick
.
value
})
//最后提交保存在数据库中
if
(
save
)
{
//item_num 游戏项目(1到6)
//wechat 原始token
sendGameMessage
(
'submit_score_save'
,
{
score
:
tick
.
value
,
wechat
:
wechat
.
token_origin
,
item_num
:
1
,
nickname
:
wechat
.
nickname
,
avatar
:
wechat
.
avatar
,
rank
:
rank
.
value
,
})
}
else
{
sendGameMessage
(
'submit_score'
,
{
score
:
tick
.
value
})
}
}
}
function
startGameCountdown
(
seconds
=
60
)
{
function
startGameCountdown
(
seconds
=
60
)
{
...
@@ -71,7 +90,7 @@ function startGameCountdown(seconds = 60) {
...
@@ -71,7 +90,7 @@ function startGameCountdown(seconds = 60) {
countdownInterval
.
value
=
0
countdownInterval
.
value
=
0
gameCountdownTimer
=
undefined
gameCountdownTimer
=
undefined
setDivDescVisible
(
false
)
setDivDescVisible
(
false
)
submitScore
()
submitScore
(
true
)
showScoreView
()
showScoreView
()
return
return
}
}
...
@@ -93,46 +112,24 @@ function startGameView() {
...
@@ -93,46 +112,24 @@ function startGameView() {
tick
.
value
=
0
tick
.
value
=
0
currentGu
.
value
=
guFrames
[
0
]
currentGu
.
value
=
guFrames
[
0
]
currentView
.
value
=
'playing'
currentView
.
value
=
'playing'
startGameCountdown
(
60
)
startGameCountdown
()
}
async
function
startGameWithCountdown
()
{
await
mobileStageRef
.
value
?.
startCountdown
(
3
)
startGameView
()
}
}
function
showScoreView
()
{
function
showScoreView
()
{
stopGameCountdown
()
stopGameCountdown
()
setDivDescVisible
(
false
)
setDivDescVisible
(
false
)
level
.
value
=
rank
.
value
currentView
.
value
=
'score'
currentView
.
value
=
'score'
}
}
function
updateSubmittedRank
(
data
:
any
)
{
const
nextRank
=
Number
(
typeof
data
===
'object'
?
data
?.
rank
:
data
)
if
(
Number
.
isFinite
(
nextRank
)
&&
nextRank
>
0
)
{
rank
.
value
=
nextRank
level
.
value
=
nextRank
}
}
function
replayGame
()
{
function
replayGame
()
{
startGameView
()
startGameView
()
}
}
function
backToWaiting
()
{
function
backToWaiting
()
{
showLoadingView
()
showLoadingView
()
joinSharedRoom
()
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
)
}
}
const
{
userJoinStatus
,
joinSharedRoom
,
sendGameMessage
}
=
useGameSocket
({
gameId
:
'game1'
,
onGameStart
:
startGameWithCountdown
,
onScoreSubmitted
:
updateSubmittedRank
,
onRoomClosed
:
showLoadingView
,
})
const
touchHandler
=
()
=>
{
const
touchHandler
=
()
=>
{
if
(
isGuPlaying
.
value
)
{
if
(
isGuPlaying
.
value
)
{
return
return
...
@@ -162,26 +159,65 @@ const touchHandler = () => {
...
@@ -162,26 +159,65 @@ const touchHandler = () => {
playNextFrame
()
playNextFrame
()
}
}
if
(
wechat
)
{
const
{
offSocketMessage
}
=
useGameSocket
({
gameId
:
'game1'
,
auth
:
{
userid
:
wechat
.
token
,
},
onConnect
:
()
=>
{
window
.
setTimeout
(()
=>
{
const
joined
=
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
)
},
200
)
},
onGameStart
:
async
()
=>
{
await
mobileStageRef
.
value
?.
startCountdown
()
startGameView
()
},
onScoreSubmitted
:
(
is_save
,
data
)
=>
{
if
(
!
is_save
)
{
//非保存状态下
const
nextRank
=
Number
(
typeof
data
===
'object'
?
data
?.
rank
:
data
)
if
(
Number
.
isFinite
(
nextRank
)
&&
nextRank
>
0
)
{
rank
.
value
=
nextRank
}
}
},
onRescoreSubmitted
:
(
_recount
)
=>
{
submitScore
(
true
);
},
onRoomClosed
:
showLoadingView
,
})
_offSocketMessage
.
value
=
offSocketMessage
}
onMounted
(()
=>
{
onMounted
(()
=>
{
window
.
addEventListener
(
'beforeunload'
,
confirmRefresh
)
if
(
token
.
value
)
{
window
.
addEventListener
(
'beforeunload'
,
confirmRefresh
)
}
})
})
onBeforeUnmount
(()
=>
{
onBeforeUnmount
(()
=>
{
_offSocketMessage
.
value
?.()
if
(
guFrameTimer
)
{
if
(
guFrameTimer
)
{
window
.
clearTimeout
(
guFrameTimer
)
window
.
clearTimeout
(
guFrameTimer
)
}
}
stopGameCountdown
()
stopGameCountdown
()
window
.
removeEventListener
(
'beforeunload'
,
confirmRefresh
)
if
(
token
.
value
)
{
window
.
removeEventListener
(
'beforeunload'
,
confirmRefresh
)
}
})
})
</
script
>
</
script
>
<
template
>
<
template
>
<MobileStage
ref=
"mobileStageRef"
:background=
"`$
{imageUrls.bg} center/cover`">
<MobileStage
v-if=
"token"
ref=
"mobileStageRef"
:background=
"`$
{imageUrls.bg} center/cover`">
<LoadingView
v-if=
"currentView === 'loading'"
:image-urls=
"imageUrls"
:user-join-status=
"userJoinStatus"
/>
<LoadingView
v-if=
"currentView === 'loading'"
:image-urls=
"imageUrls"
:user-join-status=
"userJoinStatus"
/>
<PlayingView
v-else-if=
"currentView === 'playing'"
:image-urls=
"imageUrls"
:tick=
"tick"
:rank=
"rank"
<PlayingView
v-else-if=
"currentView === 'playing'"
:image-urls=
"imageUrls"
:tick=
"tick"
:rank=
"rank"
:current-gu=
"currentGu"
:countdown-interval=
"countdownInterval"
:is-div-desc-visible=
"isDivDescVisible"
:current-gu=
"currentGu"
:countdown-interval=
"countdownInterval"
:is-div-desc-visible=
"isDivDescVisible"
@
touch=
"touchHandler"
/>
@
touch=
"touchHandler"
/>
<ScoreView
v-else
:image-urls=
"imageUrls"
:
tick=
"tick"
:level=
"level
"
@
replay=
"replayGame"
<ScoreView
v-else
:image-urls=
"imageUrls"
:
rank=
"rank"
:tick=
"tick
"
@
replay=
"replayGame"
@
back=
"backToWaiting"
/>
@
back=
"backToWaiting"
/>
</MobileStage>
</MobileStage>
<div
v-else
style=
"text-align: center; width: 100vw; height: 100vh; line-height: 30; font-size: 20px;"
>
请使用微信扫码进入游戏
</div>
</
template
>
</
template
>
frontend-h5/src/pages/game1/views/LoadingView.vue
View file @
8a5923a5
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
$getWechat
}
from
'@/commons/utils.ts'
import
{
onMounted
,
ref
}
from
'vue'
;
defineProps
<
{
defineProps
<
{
imageUrls
:
Record
<
string
,
string
>
imageUrls
:
Record
<
string
,
string
>
// countdownInterval: number
// isDivDescVisible: boolean
userJoinStatus
:
boolean
userJoinStatus
:
boolean
}
>
()
}
>
();
const
nickname
=
ref
(
''
);
const
avatar
=
ref
(
''
);
onMounted
(()
=>
{
const
wechat
=
$getWechat
();
nickname
.
value
=
wechat
.
nickname
;
avatar
.
value
=
wechat
.
avatar
;
});
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"h5-page game-stage"
>
<div
class=
"h5-page game-stage"
>
<!--
<Transition
name=
"div-desc-slide"
appear
>
<div
v-if=
"isDivDescVisible"
class=
"div-desc"
>
<span
class=
"desc-clock"
aria-hidden=
"true"
></span>
<span
class=
"desc-time"
>
{{
countdownInterval
}}
</span>
</div>
</Transition>
-->
<div
class=
"game-desc"
>
游戏规则
</div>
<div
class=
"game-desc"
>
游戏规则
</div>
<div
class=
"img-logo"
></div>
<div
class=
"img-logo"
></div>
<div
class=
"img-title"
></div>
<div
class=
"img-title"
></div>
<div
class=
"img-avatar"
></div>
<div
class=
"img-avatar"
:style=
"`background: url($
{avatar});border-radius:66px;width:132px;height:132px;`"
>
</div>
<div
class=
"txt-nickname"
>
张小明
</div>
<div
class=
"txt-nickname"
>
{{
nickname
}}
</div>
<div
class=
"txt-loading"
>
<div
class=
"txt-loading"
>
<label
v-if=
"userJoinStatus"
>
您已成功加入游戏等待主持人开始
</label>
<label
v-if=
"userJoinStatus"
>
您已成功加入游戏等待主持人开始
</label>
<label
v-else
>
正在加入游戏...
</label>
<label
v-else
>
正在加入游戏...
</label>
...
@@ -37,64 +39,6 @@ defineProps<{
...
@@ -37,64 +39,6 @@ defineProps<{
padding
:
0
;
padding
:
0
;
}
}
/* .div-desc {
position: absolute;
top: 150px;
left: var(--stage-viewport-left, 0);
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
width: 180px;
height: 80px;
border-radius: 0 56px 56px 0;
background: rgba(40, 12, 8, 0.48);
color: #fff;
.desc-clock {
position: absolute;
left: 20px;
width: 38px;
height: 44px;
border-radius: 50%;
background: v-bind('imageUrls.clock') center/cover;
}
.desc-time {
position: absolute;
right: 40px;
color: white;
font-size: 35pt;
line-height: 1;
}
} */
/*
.div-desc-slide-enter-active {
transition:
transform 360ms ease-out 220ms,
opacity 360ms ease-out 220ms;
}
.div-desc-slide-leave-active {
transition:
transform 260ms ease-in,
opacity 260ms ease-in;
}
.div-desc-slide-enter-from,
.div-desc-slide-leave-to {
opacity: 0;
transform: translateX(-110%);
}
.div-desc-slide-enter-to,
.div-desc-slide-leave-from {
opacity: 1;
transform: translateX(0);
} */
.game-desc
{
.game-desc
{
position
:
absolute
;
position
:
absolute
;
top
:
50%
;
top
:
50%
;
...
@@ -150,15 +94,15 @@ defineProps<{
...
@@ -150,15 +94,15 @@ defineProps<{
position
:
absolute
;
position
:
absolute
;
top
:
480px
;
top
:
480px
;
left
:
50%
;
left
:
50%
;
width
:
148px
;
/*
width: 148px;
height: 148px;
height: 148px;
background
:
v-bind
(
'
imageUrls.avatar'
)
center
/
cover
;
background: v-bind('
avatar') center/cover; */
transform
:
translateX
(
-50%
);
transform
:
translateX
(
-50%
);
}
}
.txt-nickname
{
.txt-nickname
{
position
:
absolute
;
position
:
absolute
;
top
:
6
5
0px
;
top
:
6
4
0px
;
left
:
50%
;
left
:
50%
;
color
:
#E00000
;
color
:
#E00000
;
font-size
:
25pt
;
font-size
:
25pt
;
...
...
frontend-h5/src/pages/game1/views/ScoreView.vue
View file @
8a5923a5
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
defineProps
<
{
defineProps
<
{
imageUrls
:
Record
<
string
,
string
>
imageUrls
:
Record
<
string
,
string
>
tick
:
number
tick
:
number
level
:
number
rank
:
number
}
>
()
}
>
()
defineEmits
<
{
defineEmits
<
{
...
@@ -23,7 +23,7 @@ defineEmits<{
...
@@ -23,7 +23,7 @@ defineEmits<{
<div>
击鼓次数(次)
</div>
<div>
击鼓次数(次)
</div>
<div>
{{
tick
}}
</div>
<div>
{{
tick
}}
</div>
<div>
最终排名
</div>
<div>
最终排名
</div>
<div>
第
<label>
{{
level
}}
</label>
名
</div>
<div>
第
<label>
{{
rank
}}
</label>
名
</div>
</div>
</div>
</div>
</div>
<div
class=
"btn-group"
>
<div
class=
"btn-group"
>
...
...
frontend-h5/src/router/index.ts
View file @
8a5923a5
import
{
createRouter
,
createWebHashHistory
}
from
'vue-router'
import
{
createRouter
,
createWebHashHistory
}
from
'vue-router'
import
{
$read
}
from
'@/commons/utils'
import
{
$read
}
from
'@/commons/utils'
import
Login
from
'@/pages/Login.vue'
import
Game1
from
'@/pages/game1/Game1.vue'
import
Game1
from
'@/pages/game1/Game1.vue'
const
getWechatId
=
()
=>
{
const
getWechatId
=
()
=>
{
return
$read
(
'
wechat_id
'
,
''
)
return
$read
(
'
token
'
,
''
)
}
}
const
router
=
createRouter
({
const
router
=
createRouter
({
history
:
createWebHashHistory
(),
history
:
createWebHashHistory
(),
routes
:
[
routes
:
[
{
// {
path
:
'/'
,
// path: '/',
redirect
:
()
=>
(
getWechatId
()
?
'/game1'
:
'/login'
),
// redirect: () => (getWechatId() ? '/game1' : '/login'),
},
// },
{
path
:
'/login'
,
name
:
'Login'
,
component
:
Login
,
},
{
{
path
:
'/game1'
,
path
:
'/game1'
,
name
:
'Game1'
,
name
:
'Game1'
,
component
:
Game1
,
component
:
Game1
,
meta
:
{
//
meta: {
requiresAuth
:
true
,
//
requiresAuth: true,
},
//
},
},
},
// {
// {
// path: '/:pathMatch(.*)*',
// path: '/:pathMatch(.*)*',
...
...
frontend-h5/vite.config.ts
View file @
8a5923a5
...
@@ -6,7 +6,7 @@ import { defineConfig } from 'vite'
...
@@ -6,7 +6,7 @@ import { defineConfig } from 'vite'
import
vue
from
'@vitejs/plugin-vue'
import
vue
from
'@vitejs/plugin-vue'
import
vueDevTools
from
'vite-plugin-vue-devtools'
import
vueDevTools
from
'vite-plugin-vue-devtools'
const
buildDomain
=
'https://hddp
server
.guocai365.org.cn'
const
buildDomain
=
'https://hddp.guocai365.org.cn'
function
copyImageAssets
()
{
function
copyImageAssets
()
{
return
{
return
{
...
...
frontend-large/src/assets/images/game1.mp3
0 → 100644
View file @
8a5923a5
File added
frontend-large/src/assets/images/game1/qrcode.png
View replaced file @
948fdad3
View file @
8a5923a5
31.3 KB
|
W:
|
H:
695 Bytes
|
W:
|
H:
2-up
Swipe
Onion skin
frontend-large/src/commons/music.ts
0 → 100644
View file @
8a5923a5
import
game1MusicUrl
from
'@/assets/images/game1.mp3'
let
backgroundMusic
:
HTMLAudioElement
|
null
=
null
function
getBackgroundMusic
()
{
if
(
!
backgroundMusic
)
{
backgroundMusic
=
new
Audio
(
game1MusicUrl
)
backgroundMusic
.
loop
=
true
backgroundMusic
.
preload
=
'auto'
backgroundMusic
.
volume
=
0.6
backgroundMusic
.
load
()
}
return
backgroundMusic
}
export
async
function
playGame1Music
()
{
const
audio
=
getBackgroundMusic
()
try
{
await
audio
.
play
()
window
.
removeEventListener
(
'pointerdown'
,
playGame1Music
)
window
.
removeEventListener
(
'keydown'
,
playGame1Music
)
}
catch
{
window
.
addEventListener
(
'pointerdown'
,
playGame1Music
,
{
once
:
true
})
window
.
addEventListener
(
'keydown'
,
playGame1Music
,
{
once
:
true
})
}
}
export
function
stopGame1Music
()
{
window
.
removeEventListener
(
'pointerdown'
,
playGame1Music
)
window
.
removeEventListener
(
'keydown'
,
playGame1Music
)
backgroundMusic
?.
pause
()
}
frontend-large/src/commons/utils.ts
View file @
8a5923a5
...
@@ -74,7 +74,7 @@ export function $read_socket_storge(): any {
...
@@ -74,7 +74,7 @@ export function $read_socket_storge(): any {
let
token
=
$read
(
'token'
,
''
);
let
token
=
$read
(
'token'
,
''
);
const
socket_path
=
$read
(
'socket_path'
,
''
);
const
socket_path
=
$read
(
'socket_path'
,
''
);
const
client_ns
=
$read
(
'client_ns'
,
''
)
const
client_ns
=
$read
(
'client_ns'
,
''
)
const
domain
=
localStorage
.
getItem
(
'domain'
)
||
''
const
domain
=
$read
(
'domain'
,
''
);
return
{
return
{
passphrase
,
token
,
socket_path
,
client_ns
,
domain
passphrase
,
token
,
socket_path
,
client_ns
,
domain
}
}
...
@@ -88,8 +88,17 @@ export function $format_str(str: string, num: number) {
...
@@ -88,8 +88,17 @@ export function $format_str(str: string, num: number) {
}
}
export
function
$is_run_local
():
boolean
{
export
function
$is_run_local
():
boolean
{
if
(
/localhost|192.168/gim
.
test
(
location
.
href
))
{
if
(
/
https
?
:
\/\/
localhost|192.168/gim
.
test
(
location
.
href
))
{
return
true
;
return
true
;
}
}
return
false
;
return
false
;
}
}
export
function
$query
(
q
:
string
|
string
[]):
string
|
string
[]
{
const
query
=
location
.
href
.
match
(
/
\?([^
#
]
*
)
/
)?.[
1
]?.
replace
(
/
\/
$/
,
''
);
const
params
=
new
URLSearchParams
(
query
);
if
(
Array
.
isArray
(
q
))
{
return
q
.
map
((
key
)
=>
params
.
get
(
key
)).
filter
((
v
):
v
is
string
=>
!!
v
);
}
return
params
.
get
(
q
)
||
''
;
}
\ No newline at end of file
frontend-large/src/commons/ws.ts
View file @
8a5923a5
import
{
io
,
type
Socket
}
from
"socket.io-client"
;
import
{
io
,
type
Socket
}
from
"socket.io-client"
;
import
{
decode
,
encode
}
from
"@msgpack/msgpack"
;
import
{
decode
,
encode
}
from
"@msgpack/msgpack"
;
import
{
$is_run_local
}
from
"./utils"
;
export
type
SocketIOClientOptions
=
{
export
type
SocketIOClientOptions
=
{
url
?:
string
;
url
?:
string
;
...
@@ -36,7 +37,6 @@ export function initSocket(args: SocketInitArgs): Socket | null {
...
@@ -36,7 +37,6 @@ export function initSocket(args: SocketInitArgs): Socket | null {
args
.
onConnect
?.(
socket
);
args
.
onConnect
?.(
socket
);
return
socket
;
return
socket
;
}
}
socket
.
removeAllListeners
();
socket
.
removeAllListeners
();
socket
.
disconnect
();
socket
.
disconnect
();
socket
=
null
;
socket
=
null
;
...
@@ -56,7 +56,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
...
@@ -56,7 +56,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
});
});
socket
.
onAny
((
evt
,
payload
)
=>
{
socket
.
onAny
((
evt
,
payload
)
=>
{
const
data
=
decode
(
payload
);
const
data
=
decode
(
payload
);
console
.
log
(
evt
,
data
);
if
(
$is_run_local
())
{
console
.
log
(
evt
,
data
);
}
messageHandlers
.
forEach
((
handler
)
=>
handler
(
evt
,
data
));
messageHandlers
.
forEach
((
handler
)
=>
handler
(
evt
,
data
));
});
});
...
...
frontend-large/src/composables/useAdminGameSocket.ts
View file @
8a5923a5
...
@@ -45,7 +45,7 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
...
@@ -45,7 +45,7 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
const
{
passphrase
,
token
,
socket_path
,
client_ns
,
domain
}
=
$read_socket_storge
()
const
{
passphrase
,
token
,
socket_path
,
client_ns
,
domain
}
=
$read_socket_storge
()
initSocket
({
initSocket
({
socket_path
:
`
${
domain
}${
socket_path
}
`
,
socket_path
:
`
${
domain
}${
socket_path
}
`
,
client_ns
,
client_ns
:
"/ws"
,
auth
:
{
auth
:
{
userid
:
token
,
userid
:
token
,
validuser
:
CryptoJS
.
AES
.
encrypt
(
token
,
passphrase
).
toString
(),
validuser
:
CryptoJS
.
AES
.
encrypt
(
token
,
passphrase
).
toString
(),
...
@@ -58,9 +58,9 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
...
@@ -58,9 +58,9 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
offSocketMessage
=
onSocketMessage
(
async
(
evt
,
payload
:
SocketPayload
)
=>
{
offSocketMessage
=
onSocketMessage
(
async
(
evt
,
payload
:
SocketPayload
)
=>
{
const
{
msg
,
state
,
data
}
=
payload
const
{
msg
,
state
,
data
}
=
payload
if
(
$goto_login
(
evt
,
router
,
options
.
loginRedirectPath
??
'/game1'
))
{
//
if ($goto_login(evt, router, options.loginRedirectPath ?? '/game1')) {
return
//
return
}
//
}
if
(
state
===
0
)
{
if
(
state
===
0
)
{
if
(
evt
===
'game_start_result'
&&
data
===
1
)
{
if
(
evt
===
'game_start_result'
&&
data
===
1
)
{
...
...
frontend-large/src/pages/Login.vue
View file @
8a5923a5
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
$save
}
from
'@/commons/utils.ts'
;
import
{
$save
}
from
'@/commons/utils.ts'
;
import
{
ref
}
from
'vue'
import
{
onMounted
,
ref
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
{
useRouter
}
from
'vue-router'
import
DesignStage
from
'@/components/DesignStage.vue'
import
DesignStage
from
'@/components/DesignStage.vue'
import
{
playGame1Music
}
from
'@/commons/music'
const
router
=
useRouter
()
const
router
=
useRouter
()
const
username
=
ref
(
''
)
const
username
=
ref
(
''
)
const
password
=
ref
(
''
)
const
password
=
ref
(
''
)
...
@@ -16,8 +17,8 @@ async function login() {
...
@@ -16,8 +17,8 @@ async function login() {
},
},
body
:
JSON
.
stringify
({
username
:
username
.
value
,
password
:
password
.
value
}),
body
:
JSON
.
stringify
({
username
:
username
.
value
,
password
:
password
.
value
}),
})
})
const
data
=
await
res
.
json
()
const
data
=
await
res
.
json
()
if
(
!
res
.
ok
||
data
.
state
!==
1
)
{
if
(
!
res
.
ok
||
data
.
state
!==
1
)
{
alert
(
data
.
msg
||
'Login failed'
)
alert
(
data
.
msg
||
'Login failed'
)
throw
new
Error
(
data
.
msg
||
'Login failed'
)
throw
new
Error
(
data
.
msg
||
'Login failed'
)
...
@@ -27,6 +28,7 @@ async function login() {
...
@@ -27,6 +28,7 @@ async function login() {
}
}
const
loginHandler
=
async
()
=>
{
const
loginHandler
=
async
()
=>
{
playGame1Music
()
const
{
state
,
msg
,
data
}
=
await
login
()
const
{
state
,
msg
,
data
}
=
await
login
()
if
(
state
===
0
)
{
if
(
state
===
0
)
{
...
@@ -38,6 +40,10 @@ const loginHandler = async () => {
...
@@ -38,6 +40,10 @@ const loginHandler = async () => {
}
}
await
router
.
replace
(
'/game1'
)
await
router
.
replace
(
'/game1'
)
}
}
onMounted
(()
=>
{
playGame1Music
()
})
</
script
>
</
script
>
<
template
>
<
template
>
...
...
frontend-large/src/pages/game1/Game1.vue
View file @
8a5923a5
...
@@ -5,6 +5,7 @@ import Rank1 from './views/Rank1View.vue'
...
@@ -5,6 +5,7 @@ import Rank1 from './views/Rank1View.vue'
import
Rank2
from
'./views/Rank2View.vue'
import
Rank2
from
'./views/Rank2View.vue'
import
{
useAdminGameSocket
}
from
'@/composables/useAdminGameSocket'
import
{
useAdminGameSocket
}
from
'@/composables/useAdminGameSocket'
import
type
Player
from
'@/commons/player.ts'
import
type
Player
from
'@/commons/player.ts'
import
{
playGame1Music
}
from
'@/commons/music'
type
GameScreen
=
'loading'
|
'rank1'
|
'rank2'
type
GameScreen
=
'loading'
|
'rank1'
|
'rank2'
...
@@ -108,6 +109,11 @@ const { startGame, createRoom, requestRoomState } = useAdminGameSocket({
...
@@ -108,6 +109,11 @@ const { startGame, createRoom, requestRoomState } = useAdminGameSocket({
onRoomRank
:
updateRankPlayers
,
onRoomRank
:
updateRankPlayers
,
})
})
const
startGameWithMusic
=
()
=>
{
playGame1Music
()
startGame
()
}
const
nextRound
=
()
=>
{
const
nextRound
=
()
=>
{
gotoLoading
()
gotoLoading
()
createRoom
(
1
)
createRoom
(
1
)
...
@@ -116,8 +122,14 @@ const nextRound = () => {
...
@@ -116,8 +122,14 @@ const nextRound = () => {
},
200
)
},
200
)
}
}
const
nextRoundWithMusic
=
()
=>
{
playGame1Music
()
nextRound
()
}
onMounted
(()
=>
{
onMounted
(()
=>
{
window
.
addEventListener
(
'beforeunload'
,
confirmRefresh
)
window
.
addEventListener
(
'beforeunload'
,
confirmRefresh
)
playGame1Music
()
})
})
onUnmounted
(()
=>
{
onUnmounted
(()
=>
{
...
@@ -126,7 +138,7 @@ onUnmounted(() => {
...
@@ -126,7 +138,7 @@ onUnmounted(() => {
</
script
>
</
script
>
<
template
>
<
template
>
<Loading
v-if=
"screen === 'loading'"
:players=
"loadingPlayers"
:player-count=
"playerCount"
@
start=
"startGame"
/>
<Loading
v-if=
"screen === 'loading'"
:players=
"loadingPlayers"
:player-count=
"playerCount"
@
start=
"startGame
WithMusic
"
/>
<Rank1
v-else-if=
"screen === 'rank1'"
ref=
"rank1Ref"
:rank-list=
"rankPlayers"
@
start=
"startGame"
/>
<Rank1
v-else-if=
"screen === 'rank1'"
ref=
"rank1Ref"
:rank-list=
"rankPlayers"
@
start=
"startGame
WithMusic
"
/>
<Rank2
v-else
:rank-list=
"rankPlayers"
@
next=
"nextRound"
/>
<Rank2
v-else
:rank-list=
"rankPlayers"
@
next=
"nextRound
WithMusic
"
/>
</
template
>
</
template
>
frontend-large/src/pages/game1/views/LoadingView.vue
View file @
8a5923a5
...
@@ -57,7 +57,7 @@ const releaseStartButton = () => {
...
@@ -57,7 +57,7 @@ const releaseStartButton = () => {
<div
v-for=
"(item, index) in players"
:key=
"item.userid || item.wechat_id || `empty-$
{index}`">
<div
v-for=
"(item, index) in players"
:key=
"item.userid || item.wechat_id || `empty-$
{index}`">
<div>
<div>
<div
class=
"img-avatar-container"
>
<div
class=
"img-avatar-container"
>
<div
class=
"img-avatar"
></div>
<div
class=
"img-avatar"
:style=
"`background:url('$
{item.avatar}') center / cover no-repeat;`"
>
</div>
</div>
</div>
<div
class=
"txt-nickname"
>
{{
$format_str
(
item
.
nickname
,
12
)
}}
</div>
<div
class=
"txt-nickname"
>
{{
$format_str
(
item
.
nickname
,
12
)
}}
</div>
</div>
</div>
...
@@ -205,7 +205,7 @@ const releaseStartButton = () => {
...
@@ -205,7 +205,7 @@ const releaseStartButton = () => {
width
:
60px
;
width
:
60px
;
height
:
60px
;
height
:
60px
;
border-radius
:
30px
;
border-radius
:
30px
;
background
:
v-bind
(
'imageUrls.avatar'
);
/* background: v-bind('imageUrls.avatar'); */
}
}
.txt-nickname
{
.txt-nickname
{
...
...
frontend-large/src/pages/game1/views/Rank2View.vue
View file @
8a5923a5
...
@@ -54,6 +54,13 @@ watch(
...
@@ -54,6 +54,13 @@ watch(
(
rankList
)
=>
updateFinalRank
(
rankList
),
(
rankList
)
=>
updateFinalRank
(
rankList
),
{
immediate
:
true
,
deep
:
true
},
{
immediate
:
true
,
deep
:
true
},
);
);
const
renderAvatar
=
(
item
:
any
|
null
)
=>
{
if
(
item
&&
item
.
avatar
)
{
return
`background: url('
${
item
.
avatar
}
') center / cover no-repeat;`
}
return
`background: v-bind('imageUrls.avatar') center / cover no-repeat;`
}
</
script
>
</
script
>
<
template
>
<
template
>
...
@@ -66,21 +73,24 @@ watch(
...
@@ -66,21 +73,24 @@ watch(
<div
class=
"podium"
>
<div
class=
"podium"
>
<div
class=
"rank-no1"
>
<div
class=
"rank-no1"
>
<div
class=
"gu-burst"
></div>
<div
class=
"gu-burst"
></div>
<div
class=
"avatar"
></div>
<div
class=
"avatar"
:style=
"`$
{renderAvatar(list[0])}`">
</div>
<div
class=
"nickname"
>
{{
list
[
0
]?.
nickname
}}
</div>
<div
class=
"nickname"
>
{{
list
[
0
]?.
nickname
}}
</div>
<div
class=
"guan"
></div>
<div
class=
"guan"
></div>
<div
class=
"pai"
></div>
<div
class=
"pai"
></div>
</div>
</div>
<div
class=
"rank-no2"
>
<div
class=
"rank-no2"
>
<div
class=
"gu-burst"
></div>
<div
class=
"gu-burst"
></div>
<div
class=
"avatar"
></div>
<div
class=
"avatar"
:style=
"`$
{renderAvatar(list[1])}`">
</div>
<div
class=
"nickname"
>
{{
list
[
1
]?.
nickname
}}
</div>
<div
class=
"nickname"
>
{{
list
[
1
]?.
nickname
}}
</div>
<div
class=
"guan"
></div>
<div
class=
"guan"
></div>
<div
class=
"pai"
></div>
<div
class=
"pai"
></div>
</div>
</div>
<div
class=
"rank-no3"
>
<div
class=
"rank-no3"
>
<div
class=
"gu-burst"
></div>
<div
class=
"gu-burst"
></div>
<div
class=
"avatar"
></div>
<div
class=
"avatar"
:style=
"`$
{renderAvatar(list[2])}`">
</div>
<div
class=
"nickname"
>
{{
list
[
2
]?.
nickname
}}
</div>
<div
class=
"nickname"
>
{{
list
[
2
]?.
nickname
}}
</div>
<div
class=
"guan"
></div>
<div
class=
"guan"
></div>
<div
class=
"pai"
></div>
<div
class=
"pai"
></div>
...
@@ -172,7 +182,7 @@ watch(
...
@@ -172,7 +182,7 @@ watch(
}
}
.avatar
{
.avatar
{
background
:
v-bind
(
'imageUrls.avatar'
)
center
/
cover
no-repeat
;
/* background: v-bind('imageUrls.avatar') center / cover no-repeat; */
width
:
120px
;
width
:
120px
;
height
:
120px
;
height
:
120px
;
border-radius
:
60px
;
border-radius
:
60px
;
...
...
frontend-large/vite.config.ts
View file @
8a5923a5
...
@@ -6,7 +6,7 @@ import { defineConfig } from 'vite'
...
@@ -6,7 +6,7 @@ import { defineConfig } from 'vite'
import
vue
from
'@vitejs/plugin-vue'
import
vue
from
'@vitejs/plugin-vue'
import
vueDevTools
from
'vite-plugin-vue-devtools'
import
vueDevTools
from
'vite-plugin-vue-devtools'
const
buildDomain
=
'https://hddp
server
.guocai365.org.cn'
const
buildDomain
=
'https://hddp.guocai365.org.cn'
function
copyImageAssets
()
{
function
copyImageAssets
()
{
return
{
return
{
...
@@ -28,7 +28,8 @@ function copyImageAssets() {
...
@@ -28,7 +28,8 @@ function copyImageAssets() {
export
default
defineConfig
(({
command
})
=>
({
export
default
defineConfig
(({
command
})
=>
({
base
:
command
===
'build'
?
'/cc/pc/'
:
'/'
,
base
:
command
===
'build'
?
'/cc/pc/'
:
'/'
,
define
:
{
define
:
{
__APP_DOMAIN__
:
JSON
.
stringify
(
command
===
'build'
?
buildDomain
:
''
),
// __APP_DOMAIN__: JSON.stringify(command === 'build' ? buildDomain : ''),
__APP_DOMAIN__
:
JSON
.
stringify
(
buildDomain
),
},
},
plugins
:
[
plugins
:
[
vue
(),
vue
(),
...
...
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