Commit 740e86b2 authored by 陈冲's avatar 陈冲

feat: 后端接口优化

parent aebc5dc7
...@@ -34,8 +34,6 @@ const GAME_RUNNING_SECS: u64 = 60; ...@@ -34,8 +34,6 @@ const GAME_RUNNING_SECS: u64 = 60;
struct RoomPlayer { struct RoomPlayer {
/// 成绩分数,具体含义由前端和游戏逻辑自行定义,后端只负责存储和排序。 /// 成绩分数,具体含义由前端和游戏逻辑自行定义,后端只负责存储和排序。
score: i64, score: i64,
/// 客户端上一次提交的累计分数,用于重连后继续累计而不是覆盖旧分数。
last_client_score: i64,
// /// wechat原始token用于记录 // /// wechat原始token用于记录
// wechat: String, // wechat: String,
/// 是否在线,离线的玩家不允许提交分数,但保留在排行榜里。 /// 是否在线,离线的玩家不允许提交分数,但保留在排行榜里。
...@@ -474,6 +472,7 @@ async fn handle_room_command( ...@@ -474,6 +472,7 @@ async fn handle_room_command(
} }
} }
state.reset(); state.reset();
state.current_game_id = Some(format!("game{index}"));
state.status = RoomStatus::Start; state.status = RoomStatus::Start;
// let snapshot = state.snapshot(); // let snapshot = state.snapshot();
drop(state); drop(state);
...@@ -493,8 +492,14 @@ async fn handle_room_command( ...@@ -493,8 +492,14 @@ async fn handle_room_command(
//超过300人的情况 //超过300人的情况
if get_player_count() > 300 { if get_player_count() > 300 {
emit_msgpack(&socket, evt, &err_resp("本场游戏人员已满", Some(json!(3)))); emit_msgpack(&socket, evt, &err_resp("本场游戏人员已满", Some(json!(3))));
_ = socket.clone().disconnect();
return; return;
} }
let gameId = data
.get("gameId")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let nickname = data let nickname = data
.get("nickname") .get("nickname")
.and_then(|value| value.as_str()) .and_then(|value| value.as_str())
...@@ -507,6 +512,22 @@ async fn handle_room_command( ...@@ -507,6 +512,22 @@ async fn handle_room_command(
.to_string(); .to_string();
if let Ok(mut state) = ROOM_STATE.lock() { if let Ok(mut state) = ROOM_STATE.lock() {
let Some(current_game_id) = state.current_game_id.clone() else {
drop(state);
return;
};
// 当前h5进入的不是大屏开启的游戏房间
if current_game_id != gameId {
drop(state);
emit_msgpack(
&socket,
evt,
&err_resp("当前游戏房间还未开启", Some(json!(2))),
);
_ = socket.clone().disconnect();
return;
}
let already_joined = state.players.contains_key(userid); let already_joined = state.players.contains_key(userid);
if already_joined { if already_joined {
if let Some(player) = state.players.get_mut(userid) { if let Some(player) = state.players.get_mut(userid) {
...@@ -532,7 +553,11 @@ async fn handle_room_command( ...@@ -532,7 +553,11 @@ async fn handle_room_command(
.and_then(|payload| payload.get("gameId")) .and_then(|payload| payload.get("gameId"))
.and_then(|value| value.as_str()) .and_then(|value| value.as_str())
{ {
emit_msgpack(&socket, "game_start_result", &ok_resp(Some(json!(game_id)))); emit_msgpack(
&socket,
"game_start_result",
&ok_resp(Some(json!(game_id))),
);
} }
} }
return; return;
...@@ -547,6 +572,7 @@ async fn handle_room_command( ...@@ -547,6 +572,7 @@ async fn handle_room_command(
Some(json!(2)), Some(json!(2)),
), ),
); );
_ = socket.clone().disconnect();
return; return;
} }
let entry_order = state.next_entry_order; let entry_order = state.next_entry_order;
...@@ -562,7 +588,6 @@ async fn handle_room_command( ...@@ -562,7 +588,6 @@ async fn handle_room_command(
}) })
.or_insert(RoomPlayer { .or_insert(RoomPlayer {
score: 0, score: 0,
last_client_score: 0,
online: true, online: true,
entry_order, entry_order,
nickname: nickname.to_string(), nickname: nickname.to_string(),
...@@ -576,7 +601,7 @@ async fn handle_room_command( ...@@ -576,7 +601,7 @@ async fn handle_room_command(
if let Some(mut player) = CLIENT_PLAYER_MAP.get_mut(userid) { if let Some(mut player) = CLIENT_PLAYER_MAP.get_mut(userid) {
player.0 = 1; player.0 = 1;
} }
emit_msgpack(&socket, evt, &ok_resp(None)); emit_msgpack(&socket, evt, &ok_resp(Some(json!(current_game_id))));
// 给所有管理员广播房间状态更新 // 给所有管理员广播房间状态更新
broadcast_msgpack_all_admin( broadcast_msgpack_all_admin(
evt, evt,
...@@ -630,9 +655,12 @@ async fn handle_room_command( ...@@ -630,9 +655,12 @@ async fn handle_room_command(
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));
_ = socket.clone().disconnect();
return;
} else if !matches!(state.status, RoomStatus::Running) { } 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));
return;
} }
/* else if !state /* else if !state
.players .players
...@@ -645,16 +673,11 @@ async fn handle_room_command( ...@@ -645,16 +673,11 @@ 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();
let score_delta = if score >= player.last_client_score { player.score = score;
score - player.last_client_score
} else {
score
};
player.score += score_delta.max(0);
player.last_client_score = score;
// player.wechat = wechat.to_string(); // player.wechat = wechat.to_string();
player.online = true; player.online = true;
drop(state); drop(state);
return;
// 方案1:每有一名玩家提交时发送给所有玩家跟管理员,玩家过多时发送太频繁, // 方案1:每有一名玩家提交时发送给所有玩家跟管理员,玩家过多时发送太频繁,
// 这里会选第2种方案, 游戏中时每秒发给所有玩家跟管理员 (push_room_score_ranking) // 这里会选第2种方案, 游戏中时每秒发给所有玩家跟管理员 (push_room_score_ranking)
...@@ -667,6 +690,7 @@ async fn handle_room_command( ...@@ -667,6 +690,7 @@ async fn handle_room_command(
} else { } else {
drop(state); drop(state);
emit_msgpack(&socket, evt, &err_resp("提交异常", None)); emit_msgpack(&socket, evt, &err_resp("提交异常", None));
return;
} }
} }
} }
...@@ -683,25 +707,34 @@ async fn handle_room_command( ...@@ -683,25 +707,34 @@ async fn handle_room_command(
if wechat.is_empty() { if wechat.is_empty() {
return; return;
} }
let (score, rank) = if let Ok(state) = ROOM_STATE.lock() { let (score, rank, ranking) = if let Ok(state) = ROOM_STATE.lock() {
//防止客户端提交的状态还在Running中 //防止客户端提交的状态还在Running中
if !matches!(state.status, RoomStatus::Submit | RoomStatus::Running) { if !matches!(state.status, RoomStatus::Submit | RoomStatus::Running) {
drop(state); drop(state);
return; return;
} }
let full_ranking = state.full_score_ranking();
let ranking = RoomState::ranking_payload(
&full_ranking
.iter()
.take(ROOM_SCORE_RANK_LIMIT)
.cloned()
.collect::<Vec<_>>(),
);
let server_score = state let server_score = state
.players .players
.get(userid) .get(userid)
.map(|player| player.score) .map(|player| player.score)
.unwrap_or(score); .unwrap_or(score);
let server_rank = state let server_rank = full_ranking
.player_rank(userid) .iter()
.map(|rank| rank as i64) .position(|player| player.wechat == userid)
.map(|index| (index + 1) as i64)
.unwrap_or(rank); .unwrap_or(rank);
drop(state); drop(state);
(server_score, server_rank) (server_score, server_rank, ranking)
} else { } else {
(score, rank) (score, rank, Vec::new())
}; };
let game = Game { let game = Game {
wechat_id: wechat.to_string(), wechat_id: wechat.to_string(),
...@@ -713,7 +746,14 @@ async fn handle_room_command( ...@@ -713,7 +746,14 @@ async fn handle_room_command(
create_date: None, create_date: None,
}; };
if game.insert().await { if game.insert().await {
emit_msgpack(&socket, evt, &ok_resp(None)); emit_msgpack(
&socket,
evt,
&ok_resp(Some(json!({
"list": ranking,
"rank": rank
}))),
);
} else { } else {
emit_msgpack(&socket, evt, &err_resp("提交异常", None)); emit_msgpack(&socket, evt, &err_resp("提交异常", None));
} }
...@@ -926,7 +966,6 @@ pub async fn login(req: &mut Request, res: &mut Response) { ...@@ -926,7 +966,6 @@ pub async fn login(req: &mut Request, res: &mut Response) {
let params = match req.parse_body::<HashMap<String, String>>().await { let params = match req.parse_body::<HashMap<String, String>>().await {
Ok(params) => params, Ok(params) => params,
Err(_) => { Err(_) => {
res.status_code(StatusCode::BAD_REQUEST);
res.render(Json(err_resp("参数格式错误", None))); res.render(Json(err_resp("参数格式错误", None)));
return; return;
} }
...@@ -942,7 +981,6 @@ pub async fn login(req: &mut Request, res: &mut Response) { ...@@ -942,7 +981,6 @@ pub async fn login(req: &mut Request, res: &mut Response) {
.unwrap_or_default(); .unwrap_or_default();
if username.is_empty() || password.is_empty() { if username.is_empty() || password.is_empty() {
res.status_code(StatusCode::BAD_REQUEST);
res.render(Json(err_resp("username 和 password 不能为空", None))); res.render(Json(err_resp("username 和 password 不能为空", None)));
return; return;
} }
...@@ -963,9 +1001,15 @@ pub async fn login(req: &mut Request, res: &mut Response) { ...@@ -963,9 +1001,15 @@ pub async fn login(req: &mut Request, res: &mut Response) {
let passphrase = utils::get_aes_passphrase(); let passphrase = utils::get_aes_passphrase();
if passphrase.is_none() { if passphrase.is_none() {
res.status_code(StatusCode::BAD_REQUEST);
res.render(Json(err_resp("username 和 password 不能为空", None))); res.render(Json(err_resp("username 和 password 不能为空", None)));
return;
} }
//当前管理员已登录,不能重复登录
if (CLIENT_ADMIN_MAP.contains_key(&manager.token)) {
res.render(Json(err_resp("当前管理员已登录,不能重复登录", None)));
return;
}
res.render(Json(ok_resp(Some(json!({ res.render(Json(ok_resp(Some(json!({
"passphrase": passphrase, "passphrase": passphrase,
"token": manager.token, "token": manager.token,
......
...@@ -12,12 +12,14 @@ ...@@ -12,12 +12,14 @@
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"msgpack": "^1.0.3", "msgpack": "^1.0.3",
"socket.io-client": "^4.8.3", "socket.io-client": "^4.8.3",
"vconsole": "^3.15.1",
"vue": "^3.5.32", "vue": "^3.5.32",
"vue-router": "^4.6.4" "vue-router": "^4.6.4"
}, },
"devDependencies": { "devDependencies": {
"@tsconfig/node24": "^24.0.4", "@tsconfig/node24": "^24.0.4",
"@types/node": "^24.12.2", "@types/node": "^24.12.2",
"@vitejs/plugin-basic-ssl": "^2.3.0",
"@vitejs/plugin-vue": "^6.0.6", "@vitejs/plugin-vue": "^6.0.6",
"@vue/tsconfig": "^0.9.1", "@vue/tsconfig": "^0.9.1",
"npm-run-all2": "^8.0.4", "npm-run-all2": "^8.0.4",
...@@ -438,6 +440,15 @@ ...@@ -438,6 +440,15 @@
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"
} }
}, },
"node_modules/@babel/runtime": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
"integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": { "node_modules/@babel/template": {
"version": "7.29.7", "version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
...@@ -929,6 +940,19 @@ ...@@ -929,6 +940,19 @@
"undici-types": "~7.16.0" "undici-types": "~7.16.0"
} }
}, },
"node_modules/@vitejs/plugin-basic-ssl": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.3.0.tgz",
"integrity": "sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
},
"peerDependencies": {
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/@vitejs/plugin-vue": { "node_modules/@vitejs/plugin-vue": {
"version": "6.0.7", "version": "6.0.7",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz",
...@@ -1334,6 +1358,29 @@ ...@@ -1334,6 +1358,29 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/copy-text-to-clipboard": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.2.tgz",
"integrity": "sha512-T6SqyLd1iLuqPA90J5N4cTalrtovCySh58iiZDGJ6FGznbclKh4UI+FGacQSgFzwKG77W7XT5gwbVEbd9cIH1A==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/core-js": {
"version": "3.49.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz",
"integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
"hasInstallScript": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
...@@ -2035,6 +2082,11 @@ ...@@ -2035,6 +2082,11 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/mutation-observer": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz",
"integrity": "sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA=="
},
"node_modules/nan": { "node_modules/nan": {
"version": "2.27.0", "version": "2.27.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz",
...@@ -2512,6 +2564,18 @@ ...@@ -2512,6 +2564,18 @@
"browserslist": ">= 4.21.0" "browserslist": ">= 4.21.0"
} }
}, },
"node_modules/vconsole": {
"version": "3.15.1",
"resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.15.1.tgz",
"integrity": "sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.17.2",
"copy-text-to-clipboard": "^3.0.1",
"core-js": "^3.11.0",
"mutation-observer": "^1.0.3"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "8.0.14", "version": "8.0.14",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz",
......
This diff is collapsed.
...@@ -8,13 +8,15 @@ const props = withDefaults(defineProps<{ ...@@ -8,13 +8,15 @@ const props = withDefaults(defineProps<{
mode?: 'contain' | 'cover' mode?: 'contain' | 'cover'
backgroundMode?: 'contain' | 'cover', backgroundMode?: 'contain' | 'cover',
showGameRule?: boolean, showGameRule?: boolean,
showGameRank?: boolean,
}>(), { }>(), {
background: 'linear-gradient(180deg, #fbf7ef 0%, #ffe7dc 100%)', background: 'linear-gradient(180deg, #fbf7ef 0%, #ffe7dc 100%)',
width: 750, width: 750,
height: 1624, height: 1624,
mode: 'contain', mode: 'contain',
backgroundMode: 'cover', backgroundMode: 'cover',
showGameRule: false showGameRule: false,
showGameRank: false
}) })
const emit = defineEmits<{ const emit = defineEmits<{
...@@ -156,6 +158,11 @@ defineExpose({ ...@@ -156,6 +158,11 @@ defineExpose({
<slot name="gameRule"></slot> <slot name="gameRule"></slot>
</div> </div>
</Transition> </Transition>
<Transition name="game-rule-fade">
<div v-if="!countdownVisible && showGameRank" class="modal-container">
<slot name="gameRank"></slot>
</div>
</Transition>
</main> </main>
</template> </template>
......
...@@ -22,6 +22,8 @@ type GameSocketOptions = { ...@@ -22,6 +22,8 @@ type GameSocketOptions = {
onRoomBack?: (data: any) => void onRoomBack?: (data: any) => void
} }
let gameId = '';
function getGameIndex(data: any) { function getGameIndex(data: any) {
if (typeof data === 'object' && data !== null) { if (typeof data === 'object' && data !== null) {
return Number(data.game ?? data.game_id ?? data.gameId ?? data.id) return Number(data.game ?? data.game_id ?? data.gameId ?? data.id)
...@@ -41,7 +43,7 @@ function getGameHomePath(data: any) { ...@@ -41,7 +43,7 @@ function getGameHomePath(data: any) {
} }
export function joinSharedRoom(nickname: string, token: string, avatar: string) { export function joinSharedRoom(nickname: string, token: string, avatar: string) {
return sendSocketMessage('room_join', { nickname: nickname, token, avatar }) return sendSocketMessage('room_join', { nickname: nickname, token, avatar, gameId: gameId })
} }
export function sendGameMessage(cmd: string, data: any = {}) { export function sendGameMessage(cmd: string, data: any = {}) {
...@@ -50,6 +52,7 @@ export function sendGameMessage(cmd: string, data: any = {}) { ...@@ -50,6 +52,7 @@ export function sendGameMessage(cmd: string, data: any = {}) {
export const userJoinStatus = ref(false) export const userJoinStatus = ref(false)
export function useGameSocket(options: GameSocketOptions) { export function useGameSocket(options: GameSocketOptions) {
gameId = options.gameId;
const router = useRouter() const router = useRouter()
let socket: Socket | null = null let socket: Socket | null = null
let offSocketMessage: (() => void) | undefined let offSocketMessage: (() => void) | undefined
...@@ -76,7 +79,10 @@ export function useGameSocket(options: GameSocketOptions) { ...@@ -76,7 +79,10 @@ export function useGameSocket(options: GameSocketOptions) {
if (evt === 'room_join_result') { if (evt === 'room_join_result') {
switch (data) { switch (data) {
case 1: userJoinStatus.value = true; break; case 1: userJoinStatus.value = true; break;
case 3: alert(msg); break; case 2:
case 3:
alert(msg);
break;
} }
} else if (evt == 'submit_score_save') { } else if (evt == 'submit_score_save') {
//提交分数的情况下有问题的情况下,只重试3次 //提交分数的情况下有问题的情况下,只重试3次
...@@ -106,6 +112,10 @@ export function useGameSocket(options: GameSocketOptions) { ...@@ -106,6 +112,10 @@ export function useGameSocket(options: GameSocketOptions) {
break break
} }
case 'room_join_result': { case 'room_join_result': {
if (data != options.gameId) {
alert('当前游戏房间还未开启');
return;
}
userJoinStatus.value = true userJoinStatus.value = true
break break
} }
......
This diff is collapsed.
...@@ -28,6 +28,7 @@ type TargetItem = { ...@@ -28,6 +28,7 @@ type TargetItem = {
} }
const list = ref<TargetItem[]>([]); const list = ref<TargetItem[]>([]);
const moleContainerRef = ref<HTMLElement | null>(null)
for (let i = 0; i < 9; i++) { for (let i = 0; i < 9; i++) {
list.value.push({ list.value.push({
id: i, id: i,
...@@ -129,6 +130,45 @@ function handleTargetTouch(index: number) { ...@@ -129,6 +130,45 @@ function handleTargetTouch(index: number) {
}, item.hitMs) }, item.hitMs)
} }
function handleBoardTouch(event: MouseEvent) {
const container = moleContainerRef.value
if (!container) {
return
}
const rect = container.getBoundingClientRect()
const x = ((event.clientX - rect.left) / rect.width) * 750
const y = ((event.clientY - rect.top) / rect.height) * 600
const colWidth = 250
const rowGap = 70
const rowHeight = (600 - rowGap * 2) / 3
const hitRadius = 225
let targetIndex = -1
let targetDistance = Number.POSITIVE_INFINITY
list.value.forEach((item, index) => {
if (!item.kind || item.state === 'idle' || item.state === 'hit') {
return
}
const col = index % 3
const row = Math.floor(index / 3)
const centerX = col * colWidth + colWidth / 2
const centerY = row * (rowHeight + rowGap) + rowHeight / 2
const distance = Math.hypot(x - centerX, y - centerY)
if (distance <= hitRadius && distance < targetDistance) {
targetIndex = index
targetDistance = distance
}
})
if (targetIndex >= 0) {
handleTargetTouch(targetIndex)
}
}
onMounted(() => { onMounted(() => {
scheduleRandomTargets() scheduleRandomTargets()
}) })
...@@ -165,16 +205,14 @@ onBeforeUnmount(() => { ...@@ -165,16 +205,14 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
<div class="mole-container"> <div ref="moleContainerRef" class="mole-container" @click="handleBoardTouch">
<div v-for="(item, index) in list" :key="item.id"> <div v-for="(item, index) in list" :key="item.id">
<div class="img-land"></div> <div class="img-land"></div>
<button v-if="item.kind" class="mole-hit-area" type="button" @click="handleTargetTouch(index)"> <span v-if="item.kind" class="img-mole" :class="[`is-${item.kind}`, `is-${item.state}`]" :style="{
<span class="img-mole" :class="[`is-${item.kind}`, `is-${item.state}`]" :style="{
'--show-duration': `${item.showMs}ms`, '--show-duration': `${item.showMs}ms`,
'--hide-duration': `${item.hideMs}ms`, '--hide-duration': `${item.hideMs}ms`,
'--hit-duration': `${item.hitMs}ms`, '--hit-duration': `${item.hitMs}ms`,
}"></span> }"></span>
</button>
</div> </div>
</div> </div>
<div class="img-hammer"></div> <div class="img-hammer"></div>
...@@ -266,18 +304,6 @@ onBeforeUnmount(() => { ...@@ -266,18 +304,6 @@ onBeforeUnmount(() => {
} }
} }
.mole-hit-area {
position: absolute;
inset: 0;
}
.mole-hit-area {
z-index: 2;
padding: 0;
border: 0;
background: transparent;
}
.img-land, .img-land,
.img-mole { .img-mole {
position: absolute; position: absolute;
......
...@@ -9,6 +9,7 @@ defineProps<{ ...@@ -9,6 +9,7 @@ defineProps<{
}>() }>()
defineEmits<{ defineEmits<{
showRank: []
replay: [] replay: []
back: [] back: []
}>() }>()
...@@ -24,23 +25,6 @@ onMounted(() => { ...@@ -24,23 +25,6 @@ onMounted(() => {
<template> <template>
<div class="h5-page game-stage"> <div class="h5-page game-stage">
<div class="img-logo"></div> <div class="img-logo"></div>
<div class="bg-bottom"></div>
<!-- <div class="img-score">
<div class="score-avatar"
:style="`background: url(${avatar});border-radius:66px;width:132px;height:132px;`"></div>
<div class="score-content">
<div>{{ nickname }}</div>
<div>您的成绩</div>
<div>击鼓次数(次)</div>
<div>{{ tick }}</div>
<div>最终排名</div>
<div><label>{{ rank }}</label></div>
</div>
</div>
<div class="btn-group">
<div class="btn-replay" @click="$emit('replay')"></div>
<div class="btn-back" @click="$emit('back')"></div>
</div> -->
</div> </div>
</template> </template>
...@@ -52,16 +36,6 @@ onMounted(() => { ...@@ -52,16 +36,6 @@ onMounted(() => {
padding: 0; padding: 0;
} }
.bg-bottom {
position: absolute;
bottom: 0;
left: var(--stage-viewport-left, 0);
width: var(--stage-viewport-width, 750px);
height: 479px;
background: v-bind('imageUrls.bg') center/cover;
opacity: 0.6;
}
.img-logo { .img-logo {
position: absolute; position: absolute;
top: 66px; top: 66px;
......
This diff is collapsed.
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
"dependencies": { "dependencies": {
"@msgpack/msgpack": "^3.1.3", "@msgpack/msgpack": "^3.1.3",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"msgpack": "^1.0.3",
"socket.io-client": "^4.8.3", "socket.io-client": "^4.8.3",
"vue": "^3.5.32", "vue": "^3.5.32",
"vue-router": "^4.6.4" "vue-router": "^4.6.4"
......
...@@ -117,7 +117,8 @@ onBeforeUnmount(() => { ...@@ -117,7 +117,8 @@ onBeforeUnmount(() => {
<template> <template>
<div class="design-stage-viewport" :style="{ background }"> <div class="design-stage-viewport" :style="{ background }">
<div class="design-stage" :style="stageStyle"> <div class="design-stage" :style="stageStyle">
<div v-if="gameCountdownVisible" class="game-countdown">{{ gameCountdown }}</div> <div v-if="gameCountdownVisible" class="game-countdown">
倒计时: &nbsp;{{ gameCountdown }}</div>
<div v-if="countdownVisible" class="countdown-modal"> <div v-if="countdownVisible" class="countdown-modal">
<div :key="countdownKey" class="countdown-number">{{ countdown }}</div> <div :key="countdownKey" class="countdown-number">{{ countdown }}</div>
</div> </div>
...@@ -159,16 +160,14 @@ onBeforeUnmount(() => { ...@@ -159,16 +160,14 @@ onBeforeUnmount(() => {
top: 64px; top: 64px;
right: 80px; right: 80px;
z-index: 10; z-index: 10;
min-width: 220px; width: 300px;
height: 132px; height: 84px;
padding: 0 40px; padding: 0 40px;
border: 6px solid rgba(255, 255, 255, 0.85);
border-radius: 66px;
background: rgba(0, 0, 0, 0.42); background: rgba(0, 0, 0, 0.42);
border: 1px dashed #ccc;
color: #fff; color: #fff;
font-size: 92px; font-size: 42px;
font-weight: 800; line-height: 84px;
line-height: 120px;
text-align: center; text-align: center;
box-shadow: 0 18px 42px rgba(0, 0, 0, 0.25); box-shadow: 0 18px 42px rgba(0, 0, 0, 0.25);
text-shadow: 0 8px 18px rgba(0, 0, 0, 0.4); text-shadow: 0 8px 18px rgba(0, 0, 0, 0.4);
......
...@@ -9,7 +9,22 @@ const imageUrls = { ...@@ -9,7 +9,22 @@ const imageUrls = {
bg1: cssAssetUrl('main-bg1.png'), bg1: cssAssetUrl('main-bg1.png'),
logo: cssAssetUrl('game1/logo.png'), logo: cssAssetUrl('game1/logo.png'),
title1: cssAssetUrl('main-title.png'), title1: cssAssetUrl('main-title.png'),
icon1: cssAssetUrl('icon1.png'),
icon2: cssAssetUrl('icon2.png'),
icon3: cssAssetUrl('icon3.png'),
icon4: cssAssetUrl('icon4.png'),
icon5: cssAssetUrl('icon5.png'),
icon6: cssAssetUrl('icon6.png'),
} }
const iconUrls = [
imageUrls.icon1,
imageUrls.icon2,
imageUrls.icon3,
imageUrls.icon4,
imageUrls.icon5,
imageUrls.icon6,
]
const list = ref(['击鼓齐福', '聚宝接福', const list = ref(['击鼓齐福', '聚宝接福',
'马上有福', '策马迎福', '圈彩纳福', '福运当头' '马上有福', '策马迎福', '圈彩纳福', '福运当头'
...@@ -49,7 +64,7 @@ const loginSuccessHandler = () => { ...@@ -49,7 +64,7 @@ const loginSuccessHandler = () => {
<div v-for="item, index in list" @click="gameHandler(index)"> <div v-for="item, index in list" @click="gameHandler(index)">
<div class="card"> <div class="card">
<div class="img"> <div class="img">
<div></div> <div :style="{ background: `${iconUrls[index]} center / cover no-repeat` }"></div>
</div> </div>
<div class="btn">{{ item }}</div> <div class="btn">{{ item }}</div>
</div> </div>
...@@ -153,13 +168,10 @@ const loginSuccessHandler = () => { ...@@ -153,13 +168,10 @@ const loginSuccessHandler = () => {
transition: transform 0.2s ease 0.1s; transition: transform 0.2s ease 0.1s;
>div { >div {
width: calc(100% - 20px); width: 100%;
height: calc(100% - 20px); height: 100%;
border: 1px solid red;
border-radius: 20px; border-radius: 20px;
position: absolute; position: absolute;
left: 10px;
top: 10px;
} }
} }
......
...@@ -148,7 +148,6 @@ const { startGame, createRoom, backRoom } = useAdminGameSocket({ ...@@ -148,7 +148,6 @@ const { startGame, createRoom, backRoom } = useAdminGameSocket({
onRelogin: async () => { onRelogin: async () => {
$remove_socket_storage(); $remove_socket_storage();
await router.replace('/') await router.replace('/')
debugger
} }
}) })
......
...@@ -141,7 +141,7 @@ function handleRoomState(data: any) { ...@@ -141,7 +141,7 @@ function handleRoomState(data: any) {
switch (data?.status) { switch (data?.status) {
case 0: case 0:
gotoLoading(true); gotoLoading(true);
createRoom(1); createRoom(4);
break; break;
case 1: case 1:
gotoLoading(); gotoLoading();
......
...@@ -121,7 +121,7 @@ function handleRoomState(data: any) { ...@@ -121,7 +121,7 @@ function handleRoomState(data: any) {
switch (data?.status) { switch (data?.status) {
case 0: case 0:
gotoLoading(true) gotoLoading(true)
createRoom(1) createRoom(6)
break break
case 1: case 1:
gotoLoading() gotoLoading()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment