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
a8efe97a
Commit
a8efe97a
authored
Jun 08, 2026
by
陈冲
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 功能修复
parent
c1dbe1ba
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
500 additions
and
187 deletions
+500
-187
socket_io.rs
backend/src/protocols/socket_io.rs
+99
-25
index.html
frontend-h5/index.html
+1
-1
base.css
frontend-h5/src/assets/base.css
+1
-0
ws.ts
frontend-h5/src/commons/ws.ts
+5
-3
MobileStage.vue
frontend-h5/src/components/MobileStage.vue
+1
-1
useGameSocket.ts
frontend-h5/src/composables/useGameSocket.ts
+14
-6
Game.vue
frontend-h5/src/pages/game1/Game.vue
+8
-11
LoadingView.vue
frontend-h5/src/pages/game1/views/LoadingView.vue
+1
-1
Game4.vue
frontend-h5/src/pages/game4/Game4.vue
+30
-34
Game5.vue
frontend-h5/src/pages/game5/Game5.vue
+11
-12
LoadingView.vue
frontend-h5/src/pages/game5/views/LoadingView.vue
+1
-0
Game.vue
frontend-h5/src/pages/game6/Game.vue
+15
-11
LoadingView.vue
frontend-h5/src/pages/game6/views/LoadingView.vue
+1
-1
vite.config.ts
frontend-h5/vite.config.ts
+1
-1
applause.mp3
frontend-large/src/assets/images/applause.mp3
+0
-0
game1.mp3
frontend-large/src/assets/images/game1.mp3
+0
-0
music.ts
frontend-large/src/commons/music.ts
+88
-1
ws.ts
frontend-large/src/commons/ws.ts
+2
-2
useAdminGameSocket.ts
frontend-large/src/composables/useAdminGameSocket.ts
+31
-11
Login.vue
frontend-large/src/pages/Login.vue
+47
-11
Main.vue
frontend-large/src/pages/Main.vue
+20
-3
Game.vue
frontend-large/src/pages/game1/Game.vue
+20
-10
Game4.vue
frontend-large/src/pages/game4/Game4.vue
+0
-0
LoadingView.vue
frontend-large/src/pages/game4/views/LoadingView.vue
+27
-18
Game5.vue
frontend-large/src/pages/game5/Game5.vue
+27
-9
LoadingView.vue
frontend-large/src/pages/game5/views/LoadingView.vue
+22
-7
Game.vue
frontend-large/src/pages/game6/Game.vue
+14
-5
Rank2View.vue
frontend-large/src/pages/game6/views/Rank2View.vue
+1
-1
index.ts
frontend-large/src/router/index.ts
+11
-1
vite.config.ts
frontend-large/vite.config.ts
+1
-1
No files found.
backend/src/protocols/socket_io.rs
View file @
a8efe97a
...
...
@@ -14,8 +14,8 @@ use std::sync::atomic::{AtomicU64, Ordering};
use
std
::
time
::{
Duration
,
Instant
};
lazy_static
::
lazy_static!
{
///
可以有多个管理员,但不要用同一个账户登录,否则后面的没效果
static
ref
CLIENT_ADMIN_
MAP
:
DashMap
<
String
,
SocketRef
>
=
DashMap
::
new
(
);
///
当前唯一在线管理员:(token, socket)。
static
ref
CLIENT_ADMIN_
SESSION
:
std
::
sync
::
Mutex
<
Option
<
(
String
,
SocketRef
)
>>
=
std
::
sync
::
Mutex
::
new
(
None
);
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
());
}
...
...
@@ -234,7 +234,10 @@ pub fn get_player_count() -> usize {
}
// 获取管理员数量
pub
fn
get_admin_count
()
->
usize
{
CLIENT_ADMIN_MAP
.len
()
CLIENT_ADMIN_SESSION
.lock
()
.map
(|
session
|
usize
::
from
(
session
.is_some
()))
.unwrap_or
(
0
)
}
pub
fn
emit_to_user
(
userid
:
&
str
,
event
:
&
str
,
payload
:
&
Value
)
{
...
...
@@ -295,11 +298,46 @@ fn broadcast_msgpack_all_player(event: &str, payload: &Value) {
}
/// 发送给所有管理员
fn
broadcast_msgpack_all_admin
(
event
:
&
str
,
payload
:
&
Value
)
{
if
let
Some
(
payload
)
=
encode_msgpack
(
payload
)
{
for
socket
in
CLIENT_ADMIN_MAP
.iter
()
{
_
=
socket
.value
()
.emit
(
event
,
&
payload
);
let
Some
(
payload
)
=
encode_msgpack
(
payload
)
else
{
return
;
};
let
socket
=
CLIENT_ADMIN_SESSION
.lock
()
.ok
()
.and_then
(|
session
|
session
.as_ref
()
.map
(|(
_
,
socket
)|
socket
.clone
()));
if
let
Some
(
socket
)
=
socket
{
_
=
socket
.emit
(
event
,
&
payload
);
}
}
fn
replace_current_admin
(
userid
:
String
,
socket
:
SocketRef
)
->
Result
<
Option
<
SocketRef
>
,
()
>
{
CLIENT_ADMIN_SESSION
.lock
()
.map
(|
mut
session
|
{
let
old_socket
=
session
.take
()
.map
(|(
_
,
socket
)|
socket
);
*
session
=
Some
((
userid
,
socket
));
old_socket
})
.map_err
(|
_
|
())
}
fn
clear_current_admin_if_match
(
userid
:
&
str
,
socket
:
&
SocketRef
)
->
bool
{
CLIENT_ADMIN_SESSION
.lock
()
.map
(|
mut
session
|
{
let
should_clear
=
session
.as_ref
()
.map
(|(
current_userid
,
current_socket
)|
{
current_userid
==
userid
&&
current_socket
.id
==
socket
.id
})
.unwrap_or
(
false
);
if
should_clear
{
*
session
=
None
;
}
should_clear
})
.unwrap_or
(
false
)
}
/// 发送给所有管理员和玩家
fn
broadcast_msgpack_all
(
event
:
&
str
,
payload
:
&
Value
)
{
...
...
@@ -503,6 +541,8 @@ async fn handle_room_command(
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
))));
}
}
}
...
...
@@ -534,6 +574,10 @@ 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
;
...
...
@@ -773,17 +817,30 @@ async fn handle_room_command(
if
!
is_admin
{
return
;
}
let
mut
status
=
""
.to_string
();
let
mut
title
=
""
.to_string
();
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
status
=
state
.current_game_id
.clone
()
.unwrap_or_default
();
title
=
get_game_tilte
(
&
status
);
state
.reset
();
drop
(
state
);
}
broadcast_msgpack_all_player
(
evt
,
&
json!
({}));
broadcast_msgpack_all_player
(
evt
,
&
err_resp
(
&
format!
(
"管理员关闭了 {} 该房间"
,
title
),
Some
(
json!
(
status
)),
),
);
broadcast_room_rank
(
Vec
::
new
());
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
None
));
}
// 获取房间状态
"room_state"
=>
{
if
let
Ok
(
state
)
=
ROOM_STATE
.lock
()
{
let
gameId
=
state
.current_game_id
.clone
()
.unwrap_or_default
();
let
count
=
state
.players
.len
();
let
ranking
=
state
.current_ranking_payload
();
let
status
=
state
.status
as
i32
;
...
...
@@ -792,7 +849,9 @@ async fn handle_room_command(
emit_msgpack
(
&
socket
,
evt
,
&
ok_resp
(
Some
(
json!
({
"status"
:
status
,
"list"
:
ranking
,
"count"
:
count
}))),
&
ok_resp
(
Some
(
json!
({
"status"
:
status
,
"list"
:
ranking
,
"count"
:
count
,
"gameId"
:
gameId
,}),
)),
);
}
}
...
...
@@ -858,24 +917,25 @@ pub async fn on_connect(_io: SocketIo, socket: SocketRef, Data(data): Data<Value
return
;
}
}
// 原子检查加写入,避免并发下同一个 userid 同时登录。
if
is_admin
{
match
CLIENT_ADMIN_MAP
.entry
(
userid
.clone
())
{
Entry
::
Occupied
(
_
)
=>
{
match
replace_current_admin
(
userid
.clone
(),
socket
.clone
())
{
Ok
(
Some
(
old_socket
)
)
=>
{
emit_msgpack
(
&
socket
,
"
log
in_result"
,
&
err_resp
(
"
当前管理员账号已登录,不能重复登录"
,
Some
(
json!
(
1
))
),
&
old_
socket
,
"
new_adm
in_result"
,
&
err_resp
(
"
有其他管理员登录,您已下线"
,
None
),
);
_
=
old_socket
.disconnect
();
}
Ok
(
None
)
=>
{}
Err
(
_
)
=>
{
_
=
socket
.disconnect
();
return
;
}
Entry
::
Vacant
(
entry
)
=>
{
entry
.insert
(
socket
.clone
());
}
}
}
else
{
if
CLIENT_ADMIN_MAP
.is_empty
()
{
if
get_admin_count
()
==
0
{
emit_msgpack
(
&
socket
,
"connect_result"
,
...
...
@@ -896,7 +956,6 @@ pub async fn on_connect(_io: SocketIo, socket: SocketRef, Data(data): Data<Value
}
}
}
// dbg!(&is_admin,&userid);
// 鉴权通过后,在这里挂玩家消息事件处理器。
let
message_userid
=
userid
.clone
();
socket
.on
(
"message"
,
move
|
socket
:
SocketRef
,
data
:
Data
<
Bytes
>
|
{
...
...
@@ -921,9 +980,11 @@ 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
{
CLIENT_ADMIN_MAP
.remove
(
&
userid
);
if
!
clear_current_admin_if_match
(
&
userid
,
&
socket
)
{
return
;
}
// 前端页面刷新时房间状态丢失体验会不好
if
CLIENT_ADMIN_MAP
.is_empty
()
{
if
get_admin_count
()
==
0
{
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
state
.reset
();
// 如果管理员都退出了,就把房间状态重置。
drop
(
state
);
...
...
@@ -1013,12 +1074,25 @@ pub async fn login(req: &mut Request, res: &mut Response) {
res
.render
(
Json
(
err_resp
(
"用户名或密码不能为空"
,
None
)));
return
;
}
//当前管理员已登录,不能重复登录
if
(
CLIENT_ADMIN_MAP
.contains_key
(
&
manager
.token
))
{
res
.render
(
Json
(
err_resp
(
"当前管理员已登录,不能重复登录"
,
None
)));
if
get_admin_count
()
>
0
{
if
let
Ok
(
state
)
=
ROOM_STATE
.lock
()
{
let
game_id
=
state
.current_game_id
.clone
()
.unwrap_or
(
""
.to_string
());
if
matches!
(
state
.status
,
RoomStatus
::
Start
|
RoomStatus
::
Running
|
RoomStatus
::
Submit
)
{
drop
(
state
);
res
.render
(
Json
(
err_resp
(
&
format!
(
"当前管理员已开启游戏房间 - {},请稍后再登录"
,
get_game_tilte
(
&
game_id
)
),
None
,
)));
return
;
}
}
}
res
.render
(
Json
(
ok_resp
(
Some
(
json!
({
"passphrase"
:
passphrase
,
"token"
:
manager
.token
,
...
...
frontend-h5/index.html
View file @
a8efe97a
...
...
@@ -2,7 +2,7 @@
<html
lang=
"zh-CN"
>
<head>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1
.0, viewport-fit=cover
"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1
, maximum-scale=1, minimum-scale=1, user-scalable=no
"
>
<title>
小游戏-H5
</title>
<style>body
{
background
:
#FA6047
;}
</style>
<script>
localStorage
.
setItem
(
'domain'
,
''
);
</script>
...
...
frontend-h5/src/assets/base.css
View file @
a8efe97a
...
...
@@ -21,6 +21,7 @@ html,
body
{
width
:
100%
;
height
:
100%
;
touch-action
:
manipulation
;
}
body
{
...
...
frontend-h5/src/commons/ws.ts
View file @
a8efe97a
...
...
@@ -67,8 +67,8 @@ export function initSocket(args: SocketInitArgs): Socket | null {
});
socket
.
onAny
((
evt
,
payload
)
=>
{
const
data
=
decode
(
payload
);
if
(
$is_run_local
())
{
console
.
log
(
evt
,
data
);
if
(
$is_run_local
())
{
console
.
log
(
"recv<============"
,
evt
,
data
);
}
messageHandlers
.
forEach
((
handler
)
=>
handler
(
evt
,
data
));
});
...
...
@@ -94,7 +94,9 @@ export function sendSocketMessage(cmd: string, data: any = {}): boolean {
}
return
false
;
}
if
(
$is_run_local
())
{
console
.
log
(
'================>send'
,
cmd
,
data
);
}
socket
.
send
(
encode
({
cmd
,
data
}));
return
true
;
}
...
...
frontend-h5/src/components/MobileStage.vue
View file @
a8efe97a
...
...
@@ -16,7 +16,7 @@ const props = withDefaults(defineProps<{
height
:
1624
,
mode
:
'contain'
,
backgroundMode
:
'cover'
,
showGameRule
:
false
,
showGameRule
:
true
,
//玩家要有操作动作才能播放音乐,这里会默认显示游戏规则,让玩家有关闭操作
showGameRank
:
false
})
...
...
frontend-h5/src/composables/useGameSocket.ts
View file @
a8efe97a
...
...
@@ -91,6 +91,12 @@ export function useGameSocket(options: GameSocketOptions) {
resubmitcount
++
;
options
.
onRescoreSubmitted
?.(
resubmitcount
);
},
200
);
}
else
if
(
evt
==
'room_close_result'
)
{
userJoinStatus
.
value
=
false
if
(
data
==
options
.
gameId
)
{
options
.
onRoomClosed
?.(
data
)
}
return
;
}
return
;
}
...
...
@@ -101,11 +107,15 @@ export function useGameSocket(options: GameSocketOptions) {
}
switch
(
evt
)
{
case
'connect_result'
:{
case
'connect_result'
:
{
debugger
break
;
}
case
'room_create_result'
:
{
if
(
`game
${
data
}
`
==
options
.
gameId
)
{
location
.
reload
();
return
;
}
const
nextPath
=
getGameHomePath
(
data
)
if
(
nextPath
&&
router
.
currentRoute
.
value
.
path
!==
nextPath
)
{
...
...
@@ -153,15 +163,13 @@ export function useGameSocket(options: GameSocketOptions) {
options
.
onScoreSubmitted
?.(
true
,
data
)
break
}
case
'room_close_result'
:
{
userJoinStatus
.
value
=
false
options
.
onRoomClosed
?.(
data
)
break
}
case
'room_back_result'
:
{
options
.
onRoomBack
?.(
data
);
break
;
}
default
:
{
debugger
}
}
options
.
onEvent
?.(
evt
,
payload
)
...
...
frontend-h5/src/pages/game1/Game.vue
View file @
a8efe97a
<
script
setup
lang=
"ts"
>
import
{
onBeforeUnmount
,
onMounted
,
ref
}
from
'vue'
import
{
onBeforeUnmount
,
onMounted
,
ref
,
watch
}
from
'vue'
import
{
cssAssetUrl
}
from
'@/commons/assets.ts'
import
MobileStage
from
'@/components/MobileStage.vue'
import
{
useGameSocket
,
joinSharedRoom
,
sendGameMessage
,
userJoinStatus
}
from
'@/composables/useGameSocket'
...
...
@@ -42,7 +42,7 @@ const isGuPlaying = ref(false)
const
token
=
ref
(
wechat
?.
token
??
''
)
const
nickname
=
ref
(
wechat
?.
nickname
??
''
)
const
avatar
=
ref
(
wechat
?.
avatar
??
''
)
const
showGameRule
=
ref
(
fals
e
)
const
showGameRule
=
ref
(
tru
e
)
const
mobileStageRef
=
ref
<
InstanceType
<
typeof
MobileStage
>
|
null
>
(
null
)
const
_offSocketMessage
=
ref
<
(()
=>
void
)
|
undefined
>
()
...
...
@@ -261,9 +261,9 @@ const showGameRuleHandler = () => {
4.禁止外挂,作弊,违规账号取消成绩与领奖资格;
<br
/><br
/>
祝您游戏愉快!
</div>
</div>
<div
class=
"rule-close"
@
click=
"showGameRuleHandler"
></div>
</div>
</div>
</
template
>
<LoadingView
v-if=
"currentView === 'loading'"
:image-urls=
"imageUrls"
:user-join-status=
"userJoinStatus"
@
touchGameRule=
"showGameRuleHandler"
/>
...
...
@@ -279,12 +279,9 @@ const showGameRuleHandler = () => {
<
style
scoped
>
.rule-container
{
.rule-txt-container
{
position
:
absolute
;
background
:
v-bind
(
'imageUrls.rule'
)
center
/
cover
no-repeat
;
width
:
692px
;
height
:
797px
;
left
:
-157px
;
top
:
-75px
;
transform
:
scale
(
0.5
);
.rule-title
{
...
...
@@ -307,11 +304,11 @@ const showGameRuleHandler = () => {
.rule-close
{
width
:
57px
;
height
:
57px
;
margin
:
0
auto
;
margin-top
:
450px
;
background
:
v-bind
(
'imageUrls.close'
)
center
/
cover
;
position
:
relative
;
transform
:
scale
(
0.8
)
;
background
:
v-bind
(
'imageUrls.close'
)
center
/
cover
no-repeat
;
transform
:
translateX
(
-50%
)
scale
(
1.4
)
;
position
:
absolute
;
bottom
:
-75px
;
left
:
50%
;
}
}
</
style
>
frontend-h5/src/pages/game1/views/LoadingView.vue
View file @
a8efe97a
...
...
@@ -23,7 +23,7 @@ onMounted(() => {
<div
class=
"img-avatar"
:style=
"`background: url($
{avatar});border-radius:66px;width:132px;height:132px;`">
</div>
<div
class=
"txt-nickname"
>
{{
nickname
}}
</div>
<div
class=
"txt-loading"
>
您已成功加入游戏等待主持人开始
</div>
<div
class=
"txt-loading"
v-if=
"userJoinStatus"
>
您已成功加入游戏等待主持人开始
</div>
<div
class=
"txt-loading2"
>
游戏即将开始 敬请期待
</div>
<div
class=
"bg-bottom"
></div>
<div
class=
"loading-gu"
></div>
...
...
frontend-h5/src/pages/game4/Game4.vue
View file @
a8efe97a
...
...
@@ -8,6 +8,7 @@ import PlayingView from './views/PlayingView.vue'
import
ScoreView
from
'./views/ScoreView.vue'
import
{
$getWechat
}
from
'@/commons/utils.ts'
const
gameId
=
'game4'
;
type
GameView
=
'loading'
|
'playing'
|
'score'
const
imageUrls
=
{
...
...
@@ -30,7 +31,9 @@ const imageUrls = {
scoreBg
:
cssAssetUrl
(
'game1/score-bg.png'
),
back
:
cssAssetUrl
(
'game4/back.svg'
),
replay
:
cssAssetUrl
(
'game4/replay.svg'
),
close
:
cssAssetUrl
(
'game4/h5-game4-close.svg'
),
close
:
cssAssetUrl
(
'game1/close.png'
),
rule
:
cssAssetUrl
(
'game1/rule.png'
)
}
const
wechat
=
$getWechat
()
...
...
@@ -52,7 +55,7 @@ const currentYaoyiyao = ref(imageUrls.yaoyiyao)
const
token
=
ref
(
wechat
?.
token
??
''
)
const
nickname
=
ref
(
wechat
?.
nickname
??
''
)
const
avatar
=
ref
(
wechat
?.
avatar
??
''
)
const
showGameRule
=
ref
(
fals
e
)
const
showGameRule
=
ref
(
tru
e
)
const
mobileStageRef
=
ref
<
InstanceType
<
typeof
MobileStage
>
|
null
>
(
null
)
const
_offSocketMessage
=
ref
<
(()
=>
void
)
|
undefined
>
()
...
...
@@ -120,6 +123,7 @@ function showLoadingView() {
stopGameCountdown
()
currentView
.
value
=
'loading'
setDivDescVisible
(
false
)
alert
(
'管理员关闭了该房间'
)
}
function
resetToLoadingView
()
{
...
...
@@ -155,13 +159,13 @@ function replayGame() {
function
backToWaiting
()
{
resetToLoadingView
()
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
)
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
,
gameId
)
}
function
handleRoomBack
()
{
resetToLoadingView
()
window
.
setTimeout
(()
=>
{
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
)
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
,
gameId
)
},
200
)
}
...
...
@@ -175,14 +179,15 @@ const showGameRuleHandler = () => {
}
if
(
wechat
)
{
const
{
offSocketMessage
}
=
useGameSocket
({
gameId
:
'game4'
,
gameId
,
auth
:
{
userid
:
wechat
.
token
,
},
onConnect
:
()
=>
{
window
.
setTimeout
(()
=>
{
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
)
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
,
gameId
)
},
200
)
},
onGameStart
:
async
()
=>
{
...
...
@@ -224,8 +229,7 @@ onBeforeUnmount(() => {
</
script
>
<
template
>
<MobileStage
v-if=
"token"
:showGameRule=
"showGameRule"
ref=
"mobileStageRef"
:background=
"stageBackground"
>
<MobileStage
v-if=
"token"
:showGameRule=
"showGameRule"
ref=
"mobileStageRef"
:background=
"stageBackground"
>
<template
#
gameRule
>
<div
class=
"rule-container"
>
<div
class=
"rule-txt-container"
>
...
...
@@ -239,9 +243,7 @@ onBeforeUnmount(() => {
5. 禁止外挂作弊,违规账号取消成绩与领奖资格;
<br
/><br
/>
祝您游戏愉快!
</div>
</div>
<div
class=
"rule-close"
>
<div
@
click=
"showGameRuleHandler"
></div>
<div
class=
"rule-close"
@
click=
"showGameRuleHandler"
></div>
</div>
</div>
</
template
>
...
...
@@ -261,42 +263,36 @@ onBeforeUnmount(() => {
<
style
scoped
>
.rule-container
{
.rule-txt-container
{
width
:
62vw
;
margin
:
0
40px
;
border-radius
:
5px
;
background
:
#FFF9E4
;
padding-top
:
20px
;
padding-bottom
:
10px
;
background
:
v-bind
(
'imageUrls.rule'
)
center
/
cover
no-repeat
;
width
:
692px
;
height
:
797px
;
transform
:
scale
(
0.5
);
.rule-title
{
text-align
:
center
;
color
:
#850000
;
color
:
white
;
font-weight
:
bold
;
font-size
:
15pt
;
font-size
:
22pt
;
margin-top
:
10px
;
letter-spacing
:
4px
;
}
.rule-txt
{
font-size
:
12
pt
;
padding
:
1
0px
;
font-size
:
26
pt
;
padding
:
4
0px
;
margin-top
:
10px
;
color
:
#AA0000
;
}
}
.rule-close
{
width
:
32px
;
height
:
130px
;
margin
:
0
auto
;
background
:
v-bind
(
'imageUrls.close'
)
center
/
cover
;
position
:
relative
;
>div
{
width
:
57px
;
height
:
57px
;
background
:
v-bind
(
'imageUrls.close'
)
center
/
cover
no-repeat
;
transform
:
translateX
(
-50%
)
scale
(
1.4
);
position
:
absolute
;
bottom
:
0
;
width
:
32px
;
height
:
32px
;
border-radius
:
16px
;
}
bottom
:
-75px
;
left
:
50%
;
}
}
</
style
>
frontend-h5/src/pages/game5/Game5.vue
View file @
a8efe97a
...
...
@@ -9,6 +9,7 @@ import ScoreView from './views/ScoreView.vue'
import
{
$getWechat
}
from
'@/commons/utils.ts'
import
{
playGame1Music
}
from
'@/commons/music'
const
gameId
=
'game5'
;
type
GameView
=
'loading'
|
'playing'
|
'score'
type
RankPlayer
=
{
rank
:
number
...
...
@@ -63,7 +64,7 @@ const isGuPlaying = ref(false)
const
token
=
ref
(
wechat
?.
token
??
''
)
const
nickname
=
ref
(
wechat
?.
nickname
??
''
)
const
avatar
=
ref
(
wechat
?.
avatar
??
''
)
const
showGameRule
=
ref
(
fals
e
)
const
showGameRule
=
ref
(
tru
e
)
const
showGameRank
=
ref
(
false
)
const
rankList
=
ref
<
RankPlayer
[]
>
([])
const
displayRankList
=
computed
<
RankPlayer
[]
>
(()
=>
{
...
...
@@ -149,6 +150,7 @@ function showLoadingView() {
stopGameCountdown
()
currentView
.
value
=
'loading'
setDivDescVisible
(
false
)
alert
(
'管理员关闭了该房间'
)
}
function
resetToLoadingView
()
{
...
...
@@ -191,7 +193,7 @@ function showGameOverRank() {
function
handleRoomBack
()
{
resetToLoadingView
()
window
.
setTimeout
(()
=>
{
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
)
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
,
gameId
)
},
200
)
}
...
...
@@ -210,7 +212,7 @@ if (wechat) {
},
onConnect
:
()
=>
{
window
.
setTimeout
(()
=>
{
const
joined
=
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
)
const
joined
=
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
,
gameId
)
},
200
)
},
onGameStart
:
async
()
=>
{
...
...
@@ -325,9 +327,9 @@ const rankCloseHandler = () => {
4.禁止外挂作弊,违规账号取消成绩与领奖资格;
<br
/><br
/>
祝您游戏愉快!
</div>
</div>
<div
class=
"rule-close"
@
click=
"showGameRuleHandler"
></div>
</div>
</div>
</
template
>
<LoadingView
v-if=
"currentView === 'loading'"
:image-urls=
"imageUrls"
:user-join-status=
"userJoinStatus"
@
touchGameRule=
"showGameRuleHandler"
/>
...
...
@@ -344,12 +346,9 @@ const rankCloseHandler = () => {
<
style
scoped
>
.rule-container
{
.rule-txt-container
{
position
:
absolute
;
background
:
v-bind
(
'imageUrls.rule'
)
center
/
cover
no-repeat
;
width
:
692px
;
height
:
797px
;
left
:
-157px
;
top
:
-75px
;
transform
:
scale
(
0.5
);
.rule-title
{
...
...
@@ -372,11 +371,11 @@ const rankCloseHandler = () => {
.rule-close
{
width
:
57px
;
height
:
57px
;
margin
:
0
auto
;
margin-top
:
450px
;
background
:
v-bind
(
'imageUrls.close'
)
center
/
cover
;
position
:
relative
;
transform
:
scale
(
0.8
)
;
background
:
v-bind
(
'imageUrls.close'
)
center
/
cover
no-repeat
;
transform
:
translateX
(
-50%
)
scale
(
1.4
)
;
position
:
absolute
;
bottom
:
-75px
;
left
:
50%
;
}
}
...
...
frontend-h5/src/pages/game5/views/LoadingView.vue
View file @
a8efe97a
...
...
@@ -145,6 +145,7 @@ onMounted(() => {
height
:
126px
;
color
:
white
;
position
:
absolute
;
z-index
:
-1
;
top
:
786px
;
left
:
50%
;
transform
:
translate
(
-50%
);
...
...
frontend-h5/src/pages/game6/Game.vue
View file @
a8efe97a
...
...
@@ -57,13 +57,20 @@ const displayRankList = computed<RankPlayer[]>(() => {
}
})
})
const
rankScore
=
computed
<
number
|
string
>
(()
=>
{
const
currentPlayer
=
rankList
.
value
.
slice
(
0
,
10
)
.
find
(
item
=>
item
.
rank
===
rank
.
value
)
return
currentPlayer
?.
score
??
score
.
value
})
// const guFrames: [string, string, string] = [imageUrls.gu01, imageUrls.gu02, imageUrls.gu03]
// const currentGu = ref(imageUrls.gu01)
const
isGuPlaying
=
ref
(
false
)
const
token
=
ref
(
wechat
?.
token
??
''
)
const
nickname
=
ref
(
wechat
?.
nickname
??
''
)
const
avatar
=
ref
(
wechat
?.
avatar
??
''
)
const
showGameRule
=
ref
(
fals
e
)
const
showGameRule
=
ref
(
tru
e
)
const
showGameRank
=
ref
(
false
)
const
mobileStageRef
=
ref
<
InstanceType
<
typeof
MobileStage
>
|
null
>
(
null
)
...
...
@@ -297,9 +304,9 @@ const rankCloseHandler = () => {
4. 禁止外挂作弊,违规账号取消成绩与领奖资格;
<br
/><br
/>
祝您游戏愉快!
</div>
</div>
<div
class=
"rule-close"
@
click=
"showGameRuleHandler"
></div>
</div>
</div>
</
template
>
<
template
#
gameRank
>
<div
class=
"ranklist-container"
>
...
...
@@ -330,7 +337,7 @@ const rankCloseHandler = () => {
<div
class=
"avatar"
:style=
"
{ backgroundImage: `url(${avatar})` }">
</div>
<div>
{{
nickname
}}
</div>
</div>
<div
class=
"col-3"
>
{{
s
core
}}
</div>
<div
class=
"col-3"
>
{{
rankS
core
}}
</div>
</div>
</div>
</div>
...
...
@@ -348,12 +355,9 @@ const rankCloseHandler = () => {
<
style
scoped
>
.rule-container
{
.rule-txt-container
{
position
:
absolute
;
background
:
v-bind
(
'imageUrls.rule'
)
center
/
cover
no-repeat
;
width
:
692px
;
height
:
797px
;
left
:
-157px
;
top
:
-75px
;
transform
:
scale
(
0.5
);
.rule-title
{
...
...
@@ -376,11 +380,11 @@ const rankCloseHandler = () => {
.rule-close
{
width
:
57px
;
height
:
57px
;
margin
:
0
auto
;
margin-top
:
450px
;
background
:
v-bind
(
'imageUrls.close'
)
center
/
cover
;
position
:
relative
;
transform
:
scale
(
0.8
)
;
background
:
v-bind
(
'imageUrls.close'
)
center
/
cover
no-repeat
;
transform
:
translateX
(
-50%
)
scale
(
1.4
)
;
position
:
absolute
;
bottom
:
-75px
;
left
:
50%
;
}
}
...
...
frontend-h5/src/pages/game6/views/LoadingView.vue
View file @
a8efe97a
...
...
@@ -77,7 +77,7 @@ onMounted(() => {
.game-desc
{
position
:
absolute
;
z-index
:
2
;
z-index
:
9999999999
;
top
:
50%
;
left
:
var
(
--stage-viewport-left
,
0
);
display
:
flex
;
...
...
frontend-h5/vite.config.ts
View file @
a8efe97a
...
...
@@ -26,7 +26,7 @@ function copyImageAssets() {
}
export
default
defineConfig
(({
command
})
=>
({
base
:
command
===
'build'
?
'/
cc/
h5/'
:
'/'
,
base
:
command
===
'build'
?
'/h5/'
:
'/'
,
define
:
{
__APP_DOMAIN__
:
JSON
.
stringify
(
command
===
'build'
?
buildDomain
:
''
),
__DEBUG__
:
JSON
.
stringify
(
command
===
'serve'
),
...
...
frontend-large/src/assets/images/applause.mp3
0 → 100644
View file @
a8efe97a
File added
frontend-large/src/assets/images/game1.mp3
View file @
a8efe97a
No preview for this file type
frontend-large/src/commons/music.ts
View file @
a8efe97a
import
game1MusicUrl
from
'@/assets/images/game1.mp3'
import
game4MusicUrl
from
'@/assets/images/game1.mp3'
// 需要改成正式的路径
import
game5MusicUrl
from
'@/assets/images/game1.mp3'
// 需要改成正式的路径
import
game6MusicUrl
from
'@/assets/images/game6.mp3'
import
go321
from
'@/assets/images/321go.mp3'
import
applause
from
'@/assets/images/applause.mp3'
let
backgroundMusic
:
HTMLAudioElement
|
null
=
null
let
countdownTemplate
:
HTMLAudioElement
|
null
=
null
let
pendingCountdownRetry
:
(()
=>
void
)
|
null
=
null
const
countdownAudios
=
new
Set
<
HTMLAudioElement
>
()
function
getBackgroundMusic
(
music
:
string
)
{
export
function
stopAllMusic
()
{
stopApplauseMusic
();
stopGame1Music
();
// stopGame2Music();
// stopGame3Music();
stopGame4Music
();
stopGame5Music
();
stopGame6Music
();
backgroundMusic
=
null
;
}
function
getBackgroundMusic
(
music
:
string
,
force
:
boolean
=
true
)
{
if
(
force
)
{
//
if
(
backgroundMusic
)
{
stopApplauseMusic
();
stopGame1Music
();
// stopGame2Music();
// stopGame3Music();
stopGame4Music
();
stopGame5Music
();
stopGame6Music
();
backgroundMusic
=
null
;
}
}
if
(
!
backgroundMusic
)
{
backgroundMusic
=
new
Audio
(
music
)
backgroundMusic
.
loop
=
true
...
...
@@ -19,6 +47,26 @@ function getBackgroundMusic(music: string) {
return
backgroundMusic
}
export
async
function
playApplauseMusic
()
{
const
audio
=
getBackgroundMusic
(
applause
)
try
{
await
audio
.
play
()
window
.
removeEventListener
(
'pointerdown'
,
playApplauseMusic
)
window
.
removeEventListener
(
'keydown'
,
playApplauseMusic
)
}
catch
{
window
.
addEventListener
(
'pointerdown'
,
playApplauseMusic
,
{
once
:
false
})
window
.
addEventListener
(
'keydown'
,
playApplauseMusic
,
{
once
:
false
})
}
}
export
function
stopApplauseMusic
()
{
window
.
removeEventListener
(
'pointerdown'
,
playApplauseMusic
)
window
.
removeEventListener
(
'keydown'
,
playApplauseMusic
)
backgroundMusic
?.
pause
()
}
export
async
function
playGame1Music
()
{
const
audio
=
getBackgroundMusic
(
game1MusicUrl
)
...
...
@@ -38,6 +86,45 @@ export function stopGame1Music() {
backgroundMusic
?.
pause
()
}
export
async
function
playGame4Music
()
{
const
audio
=
getBackgroundMusic
(
game4MusicUrl
)
try
{
await
audio
.
play
()
window
.
removeEventListener
(
'pointerdown'
,
playGame4Music
)
window
.
removeEventListener
(
'keydown'
,
playGame4Music
)
}
catch
{
window
.
addEventListener
(
'pointerdown'
,
playGame4Music
,
{
once
:
true
})
window
.
addEventListener
(
'keydown'
,
playGame4Music
,
{
once
:
true
})
}
}
export
function
stopGame4Music
()
{
window
.
removeEventListener
(
'pointerdown'
,
playGame4Music
)
window
.
removeEventListener
(
'keydown'
,
playGame4Music
)
backgroundMusic
?.
pause
()
}
export
async
function
playGame5Music
()
{
const
audio
=
getBackgroundMusic
(
game4MusicUrl
)
try
{
await
audio
.
play
()
window
.
removeEventListener
(
'pointerdown'
,
playGame5Music
)
window
.
removeEventListener
(
'keydown'
,
playGame5Music
)
}
catch
{
window
.
addEventListener
(
'pointerdown'
,
playGame5Music
,
{
once
:
true
})
window
.
addEventListener
(
'keydown'
,
playGame5Music
,
{
once
:
true
})
}
}
export
function
stopGame5Music
()
{
window
.
removeEventListener
(
'pointerdown'
,
playGame5Music
)
window
.
removeEventListener
(
'keydown'
,
playGame5Music
)
backgroundMusic
?.
pause
()
}
export
async
function
playGame6Music
()
{
const
audio
=
getBackgroundMusic
(
game6MusicUrl
)
...
...
frontend-large/src/commons/ws.ts
View file @
a8efe97a
...
...
@@ -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
(
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 @
a8efe97a
import
CryptoJS
from
'crypto-js'
import
{
onBeforeUnmount
,
onMounted
}
from
'vue'
import
{
$read_socket_storge
}
from
'@/commons/utils'
import
{
$read_socket_storge
,
$remove_socket_storage
}
from
'@/commons/utils'
import
{
initSocket
,
onSocketMessage
,
sendSocketMessage
}
from
'@/commons/ws'
import
router
from
'@/router'
type
SocketPayload
=
{
msg
?:
string
...
...
@@ -10,13 +11,14 @@ type SocketPayload = {
}
type
AdminGameSocketOptions
=
{
gameId
:
string
gameId
?:
string
,
loginRedirectPath
?:
string
onRoomState
?:
(
data
:
any
)
=>
void
onRoomJoin
?:
(
data
:
any
)
=>
void
onGameStart
?:
(
data
:
any
)
=>
void
|
Promise
<
void
>
onRoomRank
?:
(
data
:
any
)
=>
void
onRelogin
?:
()
=>
void
onAny
?:
(
evt
:
string
,
data
:
any
)
=>
void
}
export
function
useAdminGameSocket
(
options
:
AdminGameSocketOptions
)
{
...
...
@@ -41,13 +43,13 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
return
sendSocketMessage
(
cmd
,
data
)
}
function
closeRoom
(){
function
closeRoom
()
{
return
sendSocketMessage
(
'room_close'
,
{})
}
onMounted
(()
=>
{
const
{
passphrase
,
token
,
socket_path
,
client_ns
,
domain
}
=
$read_socket_storge
()
initSocket
({
let
socket
=
initSocket
({
socket_path
:
`
${
domain
}${
socket_path
}
`
,
client_ns
,
auth
:
{
...
...
@@ -67,33 +69,51 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
// }
if
(
evt
==
'login_result'
)
{
alert
(
msg
)
options
.
onRelogin
?.()
// options.onRelogin?.()
socket
?.
close
();
$remove_socket_storage
();
await
router
.
replace
(
`/login`
);
location
.
href
=
location
.
href
;
return
;
}
else
if
(
evt
==
'new_admin_result'
)
{
alert
(
msg
);
socket
?.
close
();
$remove_socket_storage
();
await
router
.
replace
(
`/login`
);
location
.
href
=
location
.
href
;
return
;
}
alert
(
msg
)
return
}
switch
(
evt
)
{
case
'room_state_result'
:
{
if
(
data
.
gameId
)
{
//如果这两个值不一致说明浏览器有直接更新游戏房间号
if
(
data
.
gameId
!=
options
.
gameId
)
{
location
.
reload
();
return
;
}
}
options
.
onRoomState
?.(
data
)
break
return
}
case
'room_join_result'
:
{
options
.
onRoomJoin
?.(
data
)
break
return
}
case
'game_start_result'
:
{
if
(
data
===
options
.
gameId
)
{
await
options
.
onGameStart
?.(
data
)
}
break
return
}
case
'room_rank_result'
:
{
options
.
onRoomRank
?.(
data
)
break
return
}
}
options
?.
onAny
?.(
evt
,
payload
);
})
})
...
...
frontend-large/src/
component
s/Login.vue
→
frontend-large/src/
page
s/Login.vue
View file @
a8efe97a
<
script
setup
lang=
"ts"
>
import
{
cssAssetUrl
}
from
'@/commons/assets'
;
import
{
$save
}
from
'@/commons/utils.ts'
;
import
{
onMounted
,
ref
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
const
emit
=
defineEmits
<
{
loginSuccess
:
[]
}
>
()
const
router
=
useRouter
()
const
username
=
ref
(
''
)
const
password
=
ref
(
''
)
const
imageUrls
=
{
bg1
:
cssAssetUrl
(
'main-bg1.png'
),
logo
:
cssAssetUrl
(
'game1/logo.png'
),
title1
:
cssAssetUrl
(
'main-title.png'
),
}
async
function
login
()
{
let
domain
=
localStorage
.
getItem
(
'domain'
)
||
''
;
const
res
=
await
fetch
(
`
${
domain
}
/manager/login`
,
{
...
...
@@ -22,11 +25,10 @@ async function login() {
})
const
data
=
await
res
.
json
()
if
(
!
res
.
ok
||
data
.
state
!==
1
)
{
alert
(
data
.
msg
||
'Login failed'
)
throw
new
Error
(
data
.
msg
||
'Login failed'
)
}
// if (!res.ok || data.state !== 1) {
// alert(data.msg || 'Login failed')
// throw new Error(data.msg || 'Login failed')
// }
return
data
}
...
...
@@ -40,7 +42,8 @@ const loginHandler = async () => {
for
(
let
i
in
data
)
{
$save
(
i
,
data
[
i
]);
}
emit
(
'loginSuccess'
);
alert
(
'登录成功'
)
await
router
.
replace
(
`/main`
);
}
onMounted
(()
=>
{
...
...
@@ -48,6 +51,9 @@ onMounted(() => {
</
script
>
<
template
>
<div
class=
"bg"
>
<div
class=
"img-logo"
></div>
<div
class=
"img-title1"
></div>
<div
class=
"login-page"
>
<form
class=
"login-panel"
@
submit
.
prevent=
"loginHandler"
>
<div
class=
"panel-header"
>
...
...
@@ -67,9 +73,40 @@ onMounted(() => {
<button
class=
"login-button"
type=
"submit"
>
登录
</button>
</form>
</div>
</div>
</
template
>
<
style
scoped
>
.bg
{
width
:
1920px
;
height
:
1080px
;
background
:
v-bind
(
'imageUrls.bg1'
)
center
/
cover
no-repeat
;
display
:
flex
;
flex-direction
:
column
;
overflow
:
hidden
;
position
:
relative
;
.img-logo
{
background
:
v-bind
(
'imageUrls.logo'
);
background-size
:
cover
;
width
:
428px
;
height
:
77px
;
position
:
absolute
;
left
:
37px
;
top
:
82px
;
}
.img-title1
{
background
:
v-bind
(
'imageUrls.title1'
)
center
/
cover
no-repeat
;
width
:
1024px
;
height
:
143px
;
margin
:
0
auto
;
position
:
relative
;
top
:
50px
;
}
}
.login-page
{
position
:
absolute
;
z-index
:
1
;
...
...
@@ -79,7 +116,6 @@ onMounted(() => {
align-items
:
center
;
justify-content
:
center
;
overflow
:
hidden
;
background
:
rgba
(
0
,
0
,
0
,
0.8
);
}
.login-panel
{
...
...
frontend-large/src/pages/Main.vue
View file @
a8efe97a
<
script
setup
lang=
"ts"
>
import
{
cssAssetUrl
}
from
'@/commons/assets.ts'
;
import
{
$read
}
from
'@/commons/utils'
;
import
{
useAdminGameSocket
}
from
'@/composables/useAdminGameSocket'
;
import
router
from
'@/router'
;
import
{
ref
}
from
'vue'
;
import
Login
from
'../components/Login.vue'
;
import
{
onMounted
,
onUnmounted
,
ref
}
from
'vue'
;
const
imageUrls
=
{
bg1
:
cssAssetUrl
(
'main-bg1.png'
),
...
...
@@ -45,10 +45,27 @@ const loginSuccessHandler = () => {
show_login
.
value
=
false
;
gameHandler
(
item_index
.
value
);
}
const
confirmRefresh
=
(
event
:
BeforeUnloadEvent
)
=>
{
event
.
preventDefault
()
event
.
returnValue
=
''
}
useAdminGameSocket
({})
onMounted
(()
=>
{
window
.
addEventListener
(
'beforeunload'
,
confirmRefresh
)
})
onUnmounted
(()
=>
{
window
.
removeEventListener
(
'beforeunload'
,
confirmRefresh
)
})
</
script
>
<
template
>
<
Login
v-if=
"show_login"
@
loginSuccess=
"loginSuccessHandler"
/
>
<
!--
<Login
v-if=
"show_login"
@
loginSuccess=
"loginSuccessHandler"
/>
--
>
<div
class=
"bg"
>
<div
class=
"img-logo"
></div>
<div
class=
"img-title1"
></div>
...
...
frontend-large/src/pages/game1/Game.vue
View file @
a8efe97a
<
script
setup
lang=
"ts"
>
import
{
nextTick
,
onMounted
,
onUnmounted
,
ref
}
from
'vue'
import
{
nextTick
,
onMounted
,
onUnmounted
,
ref
,
watch
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
Loading
from
'./views/LoadingView.vue'
import
Rank1
from
'./views/Rank1View.vue'
import
Rank2
from
'./views/Rank2View.vue'
import
{
useAdminGameSocket
}
from
'@/composables/useAdminGameSocket'
import
type
Player
from
'@/commons/player.ts'
import
{
play
Game1
Music
,
stopGame1Music
}
from
'@/commons/music'
import
{
play
ApplauseMusic
,
playGame1Music
,
stopAll
Music
,
stopGame1Music
}
from
'@/commons/music'
import
{
$remove_socket_storage
}
from
'@/commons/utils.ts'
const
router
=
useRouter
()
...
...
@@ -114,17 +114,22 @@ const gotoRank1WithIntro = async () => {
isPlayingStartAnimation
=
false
}
function
handleRoomState
(
data
:
any
)
{
const
gameId
=
'game1'
const
{
startGame
,
createRoom
,
backRoom
,
closeRoom
}
=
useAdminGameSocket
({
gameId
,
onRoomState
:
(
data
:
any
)
=>
{
if
(
Array
.
isArray
(
data
?.
list
))
{
renderPlayers
(
data
)
}
switch
(
data
?.
status
)
{
case
0
:
gotoLoading
(
true
)
createRoom
(
1
)
break
case
1
:
// if(data.gameId && data.gameId!=gameId){
// return;
// }
gotoLoading
()
break
case
2
:
...
...
@@ -135,11 +140,7 @@ function handleRoomState(data: any) {
screen
.
value
=
'rank2'
break
}
}
const
{
startGame
,
createRoom
,
backRoom
,
closeRoom
}
=
useAdminGameSocket
({
gameId
:
'game1'
,
onRoomState
:
handleRoomState
,
},
onRoomJoin
:
(
data
)
=>
{
renderPlayers
(
data
)
gotoLoading
()
...
...
@@ -184,9 +185,18 @@ onMounted(() => {
})
onUnmounted
(()
=>
{
stop
Game1
Music
()
stop
All
Music
()
window
.
removeEventListener
(
'beforeunload'
,
confirmRefresh
)
})
watch
(
screen
,
(
value
)
=>
{
// console.log(value);
if
(
value
==
'rank2'
){
playApplauseMusic
();
}
else
if
(
value
==
'loading'
){
playGame1Music
();
}
})
</
script
>
<
template
>
...
...
frontend-large/src/pages/game4/Game4.vue
View file @
a8efe97a
This diff is collapsed.
Click to expand it.
frontend-large/src/pages/game4/views/LoadingView.vue
View file @
a8efe97a
...
...
@@ -11,6 +11,7 @@ defineProps<{
const
emit
=
defineEmits
<
{
start
:
[];
back
:[];
}
>
();
const
imageUrls
=
{
...
...
@@ -26,8 +27,10 @@ const imageUrls = {
avatarAnimation
:
cssAssetUrl
(
"avatar_animation.png"
),
avatar
:
cssAssetUrl
(
"avatar.png"
),
line
:
cssAssetUrl
(
"game1/line.png"
),
start1
:
cssAssetUrl
(
"game1/start-1.png"
),
start2
:
cssAssetUrl
(
"game1/start-2.png"
),
// start1: cssAssetUrl("game1/start-1.png"),
// start2: cssAssetUrl("game1/start-2.png"),
start
:
cssAssetUrl
(
'game1/start.png'
),
back
:
cssAssetUrl
(
'game1/back.png'
),
};
const
isStartPressed
=
ref
(
false
);
...
...
@@ -67,10 +70,7 @@ const releaseStartButton = () => {
当前在线人数
<span>
{{
playerCount
}}
</span>
</div>
<div
class=
"list-container-left"
>
<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
class=
"img-avatar-container"
>
<div
class=
"img-avatar"
:style=
"`$
{renderAvatar(item)}`">
</div>
...
...
@@ -83,14 +83,11 @@ const releaseStartButton = () => {
</div>
<div
class=
"list-container-line"
></div>
<div
class=
"list-container-right"
>
<div
class=
"btn-start"
:class=
"
{ pressed: isStartPressed }"
@pointerdown="pressStartButton"
@pointerup="releaseStartButton"
@pointerleave="releaseStartButton"
@pointercancel="releaseStartButton"
>
</div>
<!--
<div
class=
"btn-start"
:class=
"
{ pressed: isStartPressed }" @pointerdown="pressStartButton"
@pointerup="releaseStartButton" @pointerleave="releaseStartButton" @pointercancel="releaseStartButton">
</div>
-->
<div
class=
"btn-start"
@
click=
"pressStartButton"
></div>
<div
class=
"btn-back"
@
click=
"emit('back')"
></div>
</div>
</div>
</div>
...
...
@@ -148,6 +145,7 @@ const releaseStartButton = () => {
width
:
100%
;
height
:
144px
;
border-radius
:
10px
;
img
{
width
:
140px
;
height
:
140px
;
...
...
@@ -159,6 +157,7 @@ const releaseStartButton = () => {
color
:
white
;
}
}
.img-gift
{
background
:
v-bind
(
"imageUrls.gift"
)
no-repeat
;
width
:
315px
;
...
...
@@ -167,6 +166,7 @@ const releaseStartButton = () => {
top
:
124px
;
right
:
320px
;
}
.img-small-gu
{
background
:
v-bind
(
"imageUrls.smallGu"
)
no-repeat
;
width
:
161px
;
...
...
@@ -175,6 +175,7 @@ const releaseStartButton = () => {
top
:
500px
;
right
:
340px
;
}
.img-gu
{
background
:
v-bind
(
"imageUrls.gu"
)
no-repeat
;
width
:
320px
;
...
...
@@ -199,7 +200,7 @@ const releaseStartButton = () => {
color
:
#aa0000
;
font-weight
:
bold
;
>
span
{
>span
{
margin-left
:
20px
;
font-size
:
48px
;
position
:
relative
;
...
...
@@ -266,13 +267,21 @@ const releaseStartButton = () => {
width
:
316px
;
height
:
105px
;
margin-left
:
60px
;
margin-top
:
8
0px
;
background
:
v-bind
(
"imageUrls.start
1
"
);
margin-top
:
1
0px
;
background
:
v-bind
(
"imageUrls.start"
);
cursor
:
pointer
;
}
.btn-start.pressed
{
/*
.btn-start.pressed {
background: v-bind("imageUrls.start2");
} */
.btn-back
{
width
:
316px
;
height
:
105px
;
margin-left
:
60px
;
margin-top
:
30px
;
background
:
v-bind
(
'imageUrls.back'
);
cursor
:
pointer
;
}
}
}
...
...
frontend-large/src/pages/game5/Game5.vue
View file @
a8efe97a
<
script
setup
lang=
"ts"
>
import
{
nextTick
,
onMounted
,
onUnmounted
,
ref
}
from
'vue'
import
{
nextTick
,
onMounted
,
onUnmounted
,
ref
,
watch
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
Loading
from
'./views/LoadingView.vue'
import
Rank1
from
'./views/Rank1View.vue'
import
Rank2
from
'./views/Rank2View.vue'
import
{
useAdminGameSocket
}
from
'@/composables/useAdminGameSocket'
import
type
Player
from
'@/commons/player.ts'
import
{
play
Game1
Music
}
from
'@/commons/music'
import
{
play
ApplauseMusic
,
playGame5Music
,
stopAllMusic
,
stopGame5
Music
}
from
'@/commons/music'
import
{
$remove_socket_storage
}
from
'@/commons/utils.ts'
const
router
=
useRouter
()
...
...
@@ -124,7 +124,7 @@ function handleRoomState(data: any) {
switch
(
data
?.
status
)
{
case
0
:
gotoLoading
(
true
)
createRoom
(
1
)
createRoom
(
5
)
break
case
1
:
gotoLoading
()
...
...
@@ -139,7 +139,7 @@ function handleRoomState(data: any) {
}
}
const
{
startGame
,
createRoom
,
backRoom
}
=
useAdminGameSocket
({
const
{
startGame
,
createRoom
,
closeRoom
,
backRoom
}
=
useAdminGameSocket
({
gameId
:
'game5'
,
onRoomState
:
handleRoomState
,
onRoomJoin
:
(
data
)
=>
{
...
...
@@ -166,34 +166,52 @@ const startGameWithMusic = () => {
alert
(
'房间中没有玩家'
)
return
;
}
playGame
1
Music
()
playGame
5
Music
()
startGame
()
}
const
nextRound
=
()
=>
{
gotoLoading
(
true
)
//重开房间
backRoom
(
1
)
backRoom
(
5
)
}
const
nextRoundWithMusic
=
()
=>
{
playGame
1
Music
()
playGame
5
Music
()
nextRound
()
}
onMounted
(()
=>
{
window
.
addEventListener
(
'beforeunload'
,
confirmRefresh
)
playGame
1
Music
()
playGame
5
Music
()
})
onUnmounted
(()
=>
{
stopAllMusic
()
window
.
removeEventListener
(
'beforeunload'
,
confirmRefresh
)
})
const
backHandler
=
()
=>
{
if
(
confirm
(
'是否关闭当前房间回到首页'
))
{
closeRoom
();
stopGame5Music
()
router
.
replace
(
'/main'
);
}
}
watch
(
screen
,
(
value
)
=>
{
// console.log(value);
if
(
value
==
'rank2'
){
playApplauseMusic
();
}
else
if
(
value
==
'loading'
){
playGame5Music
();
}
})
</
script
>
<
template
>
<Loading
v-if=
"screen === 'loading'"
:players=
"loadingPlayers"
:player-count=
"playerCount"
@
start=
"startGameWithMusic"
/>
@
start=
"startGameWithMusic"
@
back=
"backHandler"
/>
<Rank1
v-else-if=
"screen === 'rank1'"
ref=
"rank1Ref"
:rank-list=
"rankPlayers"
@
start=
"startGameWithMusic"
@
next=
"nextRoundWithMusic"
/>
<!--
<Rank1
v-else
ref=
"rank1Ref"
:rank-list=
"rankPlayers"
@
start=
"startGameWithMusic"
--
>
...
...
frontend-large/src/pages/game5/views/LoadingView.vue
View file @
a8efe97a
...
...
@@ -11,6 +11,7 @@ defineProps<{
const
emit
=
defineEmits
<
{
start
:
[];
back
:
[];
}
>
();
const
imageUrls
=
{
...
...
@@ -25,8 +26,10 @@ const imageUrls = {
avatarAnimation
:
cssAssetUrl
(
'avatar_animation.png'
),
avatar
:
cssAssetUrl
(
'avatar.png'
),
line
:
cssAssetUrl
(
'game1/line.png'
),
start1
:
cssAssetUrl
(
'game1/start-1.png'
),
start2
:
cssAssetUrl
(
'game1/start-2.png'
),
// start1: cssAssetUrl('game1/start-1.png'),
// start2: cssAssetUrl('game1/start-2.png'),
start
:
cssAssetUrl
(
'game1/start.png'
),
back
:
cssAssetUrl
(
'game1/back.png'
),
};
const
isStartPressed
=
ref
(
false
);
...
...
@@ -74,14 +77,16 @@ const renderAvatar = (item: any | null) => {
</div>
<div
class=
"list-container-line"
></div>
<div
class=
"list-container-right"
>
<div
<
!--
<
div
class=
"btn-start"
:class=
"
{ pressed: isStartPressed }"
@pointerdown="pressStartButton"
@pointerup="releaseStartButton"
@pointerleave="releaseStartButton"
@pointercancel="releaseStartButton"
>
</div>
>
</div>
-->
<div
class=
"btn-start"
@
click=
"pressStartButton"
></div>
<div
class=
"btn-back"
@
click=
"emit('back')"
></div>
</div>
</div>
</div>
...
...
@@ -139,6 +144,7 @@ const renderAvatar = (item: any | null) => {
width
:
100%
;
height
:
144px
;
border-radius
:
10px
;
img
{
width
:
147px
;
height
:
147px
;
...
...
@@ -159,6 +165,7 @@ const renderAvatar = (item: any | null) => {
top
:
193px
;
left
:
47px
;
}
.img-right
{
background
:
v-bind
(
'imageUrls.right'
)
no-repeat
;
width
:
423px
;
...
...
@@ -251,13 +258,21 @@ const renderAvatar = (item: any | null) => {
width
:
316px
;
height
:
105px
;
margin-left
:
60px
;
margin-top
:
8
0px
;
background
:
v-bind
(
'imageUrls.start
1
'
);
margin-top
:
1
0px
;
background
:
v-bind
(
'imageUrls.start'
);
cursor
:
pointer
;
}
.btn-start.pressed
{
/*
.btn-start.pressed {
background: v-bind('imageUrls.start2');
} */
.btn-back
{
width
:
316px
;
height
:
105px
;
margin-left
:
60px
;
margin-top
:
30px
;
background
:
v-bind
(
'imageUrls.back'
);
cursor
:
pointer
;
}
}
}
...
...
frontend-large/src/pages/game6/Game.vue
View file @
a8efe97a
<
script
setup
lang=
"ts"
>
import
{
nextTick
,
onMounted
,
onUnmounted
,
ref
}
from
'vue'
import
{
nextTick
,
onMounted
,
onUnmounted
,
ref
,
watch
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
Loading
from
'./views/LoadingView.vue'
import
Rank1
from
'./views/Rank1View.vue'
import
Rank2
from
'./views/Rank2View.vue'
import
{
useAdminGameSocket
}
from
'@/composables/useAdminGameSocket'
import
type
Player
from
'@/commons/player.ts'
import
{
play
Game6
Music
,
stopGame6Music
}
from
'@/commons/music'
import
{
play
ApplauseMusic
,
playGame6Music
,
stopAll
Music
,
stopGame6Music
}
from
'@/commons/music'
import
{
$remove_socket_storage
}
from
'@/commons/utils.ts'
const
router
=
useRouter
()
...
...
@@ -135,9 +135,9 @@ function handleRoomState(data: any) {
break
}
}
const
gameId
=
'game6'
const
{
startGame
,
createRoom
,
backRoom
,
closeRoom
}
=
useAdminGameSocket
({
gameId
:
'game6'
,
gameId
,
onRoomState
:
handleRoomState
,
onRoomJoin
:
(
data
)
=>
{
renderPlayers
(
data
)
...
...
@@ -183,9 +183,18 @@ onMounted(() => {
})
onUnmounted
(()
=>
{
stop
Game6
Music
();
stop
All
Music
();
window
.
removeEventListener
(
'beforeunload'
,
confirmRefresh
)
})
watch
(
screen
,
(
value
)
=>
{
// console.log(value);
if
(
value
==
'rank2'
){
playApplauseMusic
();
}
else
if
(
value
==
'loading'
){
playGame6Music
();
}
})
</
script
>
<
template
>
...
...
frontend-large/src/pages/game6/views/Rank2View.vue
View file @
a8efe97a
...
...
@@ -89,7 +89,7 @@ const renderAvatar = (item: any | null) => {
<div
class=
"no-icon"
>
{{
index
+
4
}}
</div>
<div>
<div
class=
"img-avatar-container"
>
<div
class=
"img-avatar"
></div>
<div
class=
"img-avatar"
:style=
"`$
{renderAvatar(list[index + 3])}`"
>
</div>
</div>
<div
class=
"txt-nickname"
>
{{
item
.
nickname
}}
</div>
</div>
...
...
frontend-large/src/router/index.ts
View file @
a8efe97a
import
{
createRouter
,
createWebHashHistory
}
from
'vue-router'
import
{
$read
}
from
'@/commons/utils'
import
Login
from
'@/pages/Login.vue'
import
Main
from
'@/pages/Main.vue'
import
Game1
from
'@/pages/game1/Game.vue'
import
Game4
from
'@/pages/game4/Game4.vue'
...
...
@@ -11,12 +13,20 @@ const router = createRouter({
routes
:
[
{
path
:
'/'
,
redirect
:
()
=>
(
$read
(
'token'
,
''
)
?
'/game1'
:
'/main'
),
redirect
:
()
=>
(
$read
(
'token'
,
''
)
?
'/main'
:
'/login'
),
},
{
path
:
'/login'
,
name
:
'Login'
,
component
:
Login
,
},
{
path
:
'/main'
,
name
:
'Main'
,
component
:
Main
,
meta
:
{
requiresAuth
:
true
,
},
},
{
path
:
'/game1'
,
...
...
frontend-large/vite.config.ts
View file @
a8efe97a
...
...
@@ -26,7 +26,7 @@ function copyImageAssets() {
}
export
default
defineConfig
(({
command
})
=>
({
base
:
command
===
'build'
?
'/cc/pc/'
:
'/'
,
base
:
'/'
,
//
command === 'build' ? '/cc/pc/' : '/',
define
:
{
__APP_DOMAIN__
:
JSON
.
stringify
(
command
===
'build'
?
buildDomain
:
''
),
// __APP_DOMAIN__: JSON.stringify(buildDomain),
...
...
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