Commit 40a1a7b0 authored by 陈冲's avatar 陈冲

feat: 功能修复

parent a8efe97a
...@@ -379,6 +379,16 @@ dependencies = [ ...@@ -379,6 +379,16 @@ dependencies = [
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation"
version = "0.10.1" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
...@@ -1044,6 +1054,7 @@ dependencies = [ ...@@ -1044,6 +1054,7 @@ dependencies = [
"lazy_static", "lazy_static",
"md-5", "md-5",
"rand 0.10.0", "rand 0.10.0",
"reqwest",
"rmp-serde", "rmp-serde",
"rust-embed", "rust-embed",
"salvo", "salvo",
...@@ -1222,9 +1233,11 @@ dependencies = [ ...@@ -1222,9 +1233,11 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
"windows-registry",
] ]
[[package]] [[package]]
...@@ -2187,9 +2200,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" ...@@ -2187,9 +2200,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.13.2" version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
...@@ -2212,6 +2225,8 @@ dependencies = [ ...@@ -2212,6 +2225,8 @@ dependencies = [
"rustls", "rustls",
"rustls-pki-types", "rustls-pki-types",
"rustls-platform-verifier", "rustls-platform-verifier",
"serde",
"serde_json",
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
...@@ -2376,7 +2391,7 @@ version = "0.6.2" ...@@ -2376,7 +2391,7 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
dependencies = [ dependencies = [
"core-foundation", "core-foundation 0.10.1",
"core-foundation-sys", "core-foundation-sys",
"jni", "jni",
"log", "log",
...@@ -2607,7 +2622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -2607,7 +2622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"core-foundation", "core-foundation 0.10.1",
"core-foundation-sys", "core-foundation-sys",
"libc", "libc",
"security-framework-sys", "security-framework-sys",
...@@ -3133,6 +3148,27 @@ dependencies = [ ...@@ -3133,6 +3148,27 @@ dependencies = [
] ]
[[package]] [[package]]
name = "system-configuration"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
dependencies = [
"bitflags",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "tempfile" name = "tempfile"
version = "3.27.0" version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
...@@ -3874,6 +3910,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" ...@@ -3874,6 +3910,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]] [[package]]
name = "windows-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
dependencies = [
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-result" name = "windows-result"
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"
......
...@@ -25,6 +25,7 @@ lazy_static = "1.5.0" ...@@ -25,6 +25,7 @@ lazy_static = "1.5.0"
aes = "0.8.4" aes = "0.8.4"
cbc = { version = "0.1.2", features = ["alloc"] } cbc = { version = "0.1.2", features = ["alloc"] }
md5 = { version = "0.10.6", package = "md-5" } md5 = { version = "0.10.6", package = "md-5" }
reqwest = { version = "0.13.4", features = ["json"] }
[[bin]] [[bin]]
name = "hddpServer" name = "hddpServer"
......
...@@ -15,7 +15,8 @@ pub struct Config { ...@@ -15,7 +15,8 @@ pub struct Config {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub aes_passphrase: Option<String>, pub aes_passphrase: Option<String>,
// 数据库配置:所有 master/worker 节点都可以直连同一台 DB。 // 数据库配置:所有 master/worker 节点都可以直连同一台 DB。
pub db: DbConfig, pub db: Option<DbConfig>,
// pub post: String,
} }
#[derive(Clone, Deserialize, Serialize, Debug)] #[derive(Clone, Deserialize, Serialize, Debug)]
...@@ -34,7 +35,8 @@ impl Default for Config { ...@@ -34,7 +35,8 @@ impl Default for Config {
client_ns: "/ws".to_string(), client_ns: "/ws".to_string(),
socket_path: "/socket.io".to_string(), socket_path: "/socket.io".to_string(),
aes_passphrase: None, aes_passphrase: None,
db: DbConfig::default(), db: None, //DbConfig::default(),
// post: "".to_string(),
} }
} }
} }
......
...@@ -7,6 +7,10 @@ use base64::{Engine as _, engine::general_purpose}; ...@@ -7,6 +7,10 @@ use base64::{Engine as _, engine::general_purpose};
use cbc::Decryptor; use cbc::Decryptor;
use md5::{Digest, Md5}; use md5::{Digest, Md5};
use rand::Rng; use rand::Rng;
use reqwest::Client;
use std::option::Option;
use serde::Serialize;
use std::collections::HashMap;
use std::sync::LazyLock; use std::sync::LazyLock;
pub static CONFIG: LazyLock<Config> = LazyLock::new(|| Config::load().unwrap()); pub static CONFIG: LazyLock<Config> = LazyLock::new(|| Config::load().unwrap());
...@@ -141,3 +145,52 @@ pub fn add_minute(ts: u64, val: u64) -> u64 { ...@@ -141,3 +145,52 @@ pub fn add_minute(ts: u64, val: u64) -> u64 {
pub fn add_hour(ts: u64, val: u64) -> u64 { pub fn add_hour(ts: u64, val: u64) -> u64 {
add_minute(ts, val * 60) add_minute(ts, val * 60)
} }
// ///返回为空时表示提交异常
// pub async fn post(token: &str, index: i64) -> Result<Option<String>, Box<dyn std::error::Error>> {
// let url = CONFIG.post.clone();
// #[derive(Debug, Serialize)]
// struct RequestBody {
// activity_id: i32,
// game_code: String,
// page: i32,
// pageSize: i32,
// }
// let mut map = HashMap::new();
// map.insert(1, "JGQF");
// map.insert(2, "JBJF");
// map.insert(3, "MSYF");
// map.insert(4, "CMYF");
// map.insert(5, "QCNF");
// map.insert(6, "FYDT");
// let code = map.get(&index).and_then(|v| Some("")).unwrap();
// let body = RequestBody {
// activity_id: 1,
// game_code: code.to_string(), //大屏游戏代码 (马上有福:MSYF, 击鼓齐福:JGQF, 策马迎福:CMYF, 圈彩纳福:QCNF, 福运当头:FYDT, 聚宝接福:JBJF)
// page: 1,
// pageSize: 20,
// };
// let client = Client::new();
// let resp = client
// .post(url)
// .bearer_auth(token)
// .json(&body)
// .send()
// .await?;
// let status = resp.status();
// if !status.is_success() {
// let text = resp.text().await?;
// println!("请求失败: {}, body: {}", status, text);
// return Ok(None);
// }
// let result = resp.text().await?;
// println!("响应: {:?}", result);
// Ok(Some(result))
// }
...@@ -18,17 +18,21 @@ trait DbDriver: Send + Sync { ...@@ -18,17 +18,21 @@ trait DbDriver: Send + Sync {
static DB_CONN: OnceCell<Box<dyn DbDriver>> = OnceCell::const_new(); static DB_CONN: OnceCell<Box<dyn DbDriver>> = OnceCell::const_new();
pub async fn init_db() -> Result<(), String> { pub async fn init_db() -> Result<(), String> {
let conn: Box<dyn DbDriver> = match config.db.kind.as_str() { if let Some(db) = &config.db {
"postgresql" => Box::new(PgDb::new().await?), let conn: Box<dyn DbDriver> = match db.kind.as_str() {
"mysql" => Box::new(MysqlDb::new().await?), "postgresql" => Box::new(PgDb::new().await?),
other => { "mysql" => Box::new(MysqlDb::new().await?),
return Err(format!("unsupported db kind: {other}")); other => {
} return Err(format!("unsupported db kind: {other}"));
}; }
};
DB_CONN DB_CONN
.set(conn) .set(conn)
.map_err(|_| "db already initialized".to_string()) .map_err(|_| "db already initialized".to_string())
} else {
Ok(())
}
} }
pub async fn db_query(sql: &str, params: Vec<Value>) -> Result<Vec<Value>, String> { pub async fn db_query(sql: &str, params: Vec<Value>) -> Result<Vec<Value>, String> {
......
use crate::{commons::utils::CONFIG as config, db::models::log::Log};
use chrono::{DateTime, NaiveDate, Utc}; use chrono::{DateTime, NaiveDate, Utc};
use serde_json::json; use serde_json::json;
use crate::db::models::log::Log;
pub struct Game { pub struct Game {
pub wechat_id: String, pub wechat_id: String,
pub nickname: String, pub nickname: String,
...@@ -19,6 +18,10 @@ pub struct Game { ...@@ -19,6 +18,10 @@ pub struct Game {
impl Game { impl Game {
pub async fn insert(&self) -> bool { pub async fn insert(&self) -> bool {
let Some(_) = &config.db else {
println!("没有配置数据库, return true;");
return true;
};
let res = crate::db::db_query( let res = crate::db::db_query(
r#"insert into tb_game(wechat_id,nickname,avatar,item_num,game_rank,score,create_date) values(?,?,?,?,?,?,now());"#, r#"insert into tb_game(wechat_id,nickname,avatar,item_num,game_rank,score,create_date) values(?,?,?,?,?,?,now());"#,
vec![ vec![
......
use serde_json::{Value, json}; use crate::commons::utils::CONFIG as config;
use crate::db::models::*; use crate::db::models::*;
use serde_json::{Value, json};
#[derive(Debug)] #[derive(Debug)]
pub struct Manager { pub struct Manager {
...@@ -18,6 +18,15 @@ pub enum ManagerLogin { ...@@ -18,6 +18,15 @@ pub enum ManagerLogin {
impl Manager { impl Manager {
pub async fn Login(m: ManagerLogin) -> Result<Self, String> { pub async fn Login(m: ManagerLogin) -> Result<Self, String> {
let Some(_) = &config.db else {
println!("没有配置数据库, return Self;");
return Ok(Manager {
id: 1,
username: "admin".to_string(),
password: "123456".to_string(),
token: "88236aeb-8d3f-6d56-fdf5-3adca3961df1".to_string(),
});
};
match m { match m {
ManagerLogin::user(username, password) => { ManagerLogin::user(username, password) => {
let rows = crate::db::db_query( let rows = crate::db::db_query(
......
...@@ -21,7 +21,7 @@ impl MysqlDb { ...@@ -21,7 +21,7 @@ impl MysqlDb {
}) })
}) })
.max_connections(16) .max_connections(16)
.connect(&config.db.uri) .connect(&config.db.clone().unwrap().uri)
.await .await
.map_err(|err| err.to_string())?; .map_err(|err| err.to_string())?;
Ok(Self { pool }) Ok(Self { pool })
......
...@@ -13,7 +13,7 @@ impl PgDb { ...@@ -13,7 +13,7 @@ impl PgDb {
pub async fn new() -> Result<Self, String> { pub async fn new() -> Result<Self, String> {
let pool = PgPoolOptions::new() let pool = PgPoolOptions::new()
.max_connections(16) .max_connections(16)
.connect(&config.db.uri) .connect(&config.db.clone().unwrap().uri)
.await .await
.map_err(|err| err.to_string())?; .map_err(|err| err.to_string())?;
......
This diff is collapsed.
...@@ -63,6 +63,233 @@ export function $format_str(str: string, num: number) { ...@@ -63,6 +63,233 @@ export function $format_str(str: string, num: number) {
return str.substring(0, num) + '...'; return str.substring(0, num) + '...';
} }
let toastElement: HTMLDivElement | undefined;
let toastTimer: ReturnType<typeof window.setTimeout> | undefined;
export function $toast(message?: string, duration = 2000) {
if (!message) {
return;
}
if (!toastElement) {
toastElement = document.createElement('div');
toastElement.style.cssText = [
'position: fixed',
'left: 50%',
'top: 45%',
'z-index: 99999',
'max-width: 560px',
'padding: 18px 28px',
'border-radius: 18px',
'background: rgba(0, 0, 0, 0.72)',
'color: #fff',
'font-size: 22px',
'line-height: 1.4',
'text-align: center',
'transform: translate(-50%, -40%)',
'opacity: 0',
'pointer-events: none',
'transition: opacity 0.2s ease, transform 0.2s ease',
].join(';');
document.body.appendChild(toastElement);
}
if (toastTimer) {
window.clearTimeout(toastTimer);
toastTimer = undefined;
}
toastElement.textContent = message;
toastElement.style.opacity = '1';
toastElement.style.transform = 'translate(-50%, -50%)';
toastTimer = window.setTimeout(() => {
if (!toastElement) {
return;
}
toastElement.style.opacity = '0';
toastElement.style.transform = 'translate(-50%, -40%)';
toastTimer = undefined;
}, duration);
}
type ConfirmOptions = {
title?: string;
message?: string;
confirmText?: string;
cancelText?: string;
}
let confirmMaskElement: HTMLDivElement | undefined;
let confirmBoxElement: HTMLDivElement | undefined;
let confirmTitleElement: HTMLDivElement | undefined;
let confirmMessageElement: HTMLDivElement | undefined;
let confirmCancelElement: HTMLButtonElement | undefined;
let confirmOkElement: HTMLButtonElement | undefined;
let confirmResolve: ((value: boolean) => void) | undefined;
let confirmHideTimer: ReturnType<typeof window.setTimeout> | undefined;
function closeConfirm(result: boolean) {
if (!confirmMaskElement || !confirmBoxElement) {
confirmResolve?.(result);
confirmResolve = undefined;
return;
}
if (confirmHideTimer) {
window.clearTimeout(confirmHideTimer);
confirmHideTimer = undefined;
}
confirmMaskElement.style.opacity = '0';
confirmBoxElement.style.transform = 'scale(0.96)';
confirmHideTimer = window.setTimeout(() => {
if (confirmMaskElement) {
confirmMaskElement.style.display = 'none';
}
confirmHideTimer = undefined;
}, 200);
confirmResolve?.(result);
confirmResolve = undefined;
}
function ensureConfirmElement() {
if (confirmMaskElement) {
return;
}
confirmMaskElement = document.createElement('div');
confirmMaskElement.style.cssText = [
'position: fixed',
'inset: 0',
'z-index: 99998',
'display: none',
'align-items: center',
'justify-content: center',
'background: rgba(0, 0, 0, 0.45)',
'opacity: 0',
'transition: opacity 0.2s ease',
].join(';');
confirmBoxElement = document.createElement('div');
confirmBoxElement.style.cssText = [
'width: min(560px, calc(100vw - 80px))',
'box-sizing: border-box',
'padding: 34px 32px 28px',
'border-radius: 22px',
'background: #fff',
'color: #333',
'font-size: 24px',
'line-height: 1.45',
'text-align: center',
'box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22)',
'transform: scale(0.96)',
'transition: transform 0.2s ease',
].join(';');
confirmTitleElement = document.createElement('div');
confirmTitleElement.style.cssText = [
'margin-bottom: 16px',
'font-size: 30px',
'font-weight: 700',
'color: #222',
].join(';');
confirmMessageElement = document.createElement('div');
confirmMessageElement.style.cssText = [
'min-height: 34px',
'word-break: break-word',
].join(';');
const buttonWrap = document.createElement('div');
buttonWrap.style.cssText = [
'display: flex',
'gap: 18px',
'margin-top: 30px',
].join(';');
confirmCancelElement = document.createElement('button');
confirmCancelElement.type = 'button';
confirmCancelElement.style.cssText = [
'flex: 1',
'height: 58px',
'border: 1px solid #ddd',
'border-radius: 29px',
'background: #fff',
'color: #666',
'font-size: 24px',
].join(';');
confirmCancelElement.onclick = () => closeConfirm(false);
confirmOkElement = document.createElement('button');
confirmOkElement.type = 'button';
confirmOkElement.style.cssText = [
'flex: 1',
'height: 58px',
'border: 0',
'border-radius: 29px',
'background: #AA0000',
'color: #fff',
'font-size: 24px',
].join(';');
confirmOkElement.onclick = () => closeConfirm(true);
confirmMaskElement.onclick = (event) => {
if (event.target === confirmMaskElement) {
closeConfirm(false);
}
};
buttonWrap.append(confirmCancelElement, confirmOkElement);
confirmBoxElement.append(confirmTitleElement, confirmMessageElement, buttonWrap);
confirmMaskElement.appendChild(confirmBoxElement);
document.body.appendChild(confirmMaskElement);
}
export function $confirm(message?: string, title?: string): Promise<boolean>;
export function $confirm(options?: ConfirmOptions): Promise<boolean>;
export function $confirm(messageOrOptions?: string | ConfirmOptions, title?: string): Promise<boolean> {
const options: ConfirmOptions = typeof messageOrOptions === 'object'
? messageOrOptions
: { message: messageOrOptions, title };
if (!options.message) {
return Promise.resolve(false);
}
ensureConfirmElement();
if (confirmResolve) {
confirmResolve(false);
confirmResolve = undefined;
}
if (confirmHideTimer) {
window.clearTimeout(confirmHideTimer);
confirmHideTimer = undefined;
}
confirmTitleElement!.textContent = options.title ?? '提示';
confirmMessageElement!.textContent = options.message;
confirmCancelElement!.textContent = options.cancelText ?? '取消';
confirmOkElement!.textContent = options.confirmText ?? '确定';
confirmMaskElement!.style.display = 'flex';
window.requestAnimationFrame(() => {
if (!confirmMaskElement || !confirmBoxElement) {
return;
}
confirmMaskElement.style.opacity = '1';
confirmBoxElement.style.transform = 'scale(1)';
});
return new Promise<boolean>((resolve) => {
confirmResolve = resolve;
});
}
export function $is_run_local(): boolean { export function $is_run_local(): boolean {
if (/https?:\/\/localhost|192.168/gim.test(location.href)) { if (/https?:\/\/localhost|192.168/gim.test(location.href)) {
return true; return true;
...@@ -102,4 +329,39 @@ export function $getWechat(): any | null { ...@@ -102,4 +329,39 @@ export function $getWechat(): any | null {
} }
return null; return null;
}
export async function postJson(token: string, index: number) {
const url = "https://api.guocai365.org.cn/adminapi/offline_clearance/score/list";
let arr = ['', 'JGQF', 'JBJF', 'MSYF', 'CMYF', 'QCNF', 'FYDT'];
try {
const resp = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
activity_id: 1,
game_code: arr[index],
page: 1,
pageSize: 20,
}),
});
const text = await resp.text();
if (!resp.ok) {
console.error("请求失败:", resp.status, text);
return;
}
// 如果返回的是 JSON
const data = JSON.parse(text);
console.log("响应:", data);
} catch (err) {
console.error("请求异常:", err);
}
} }
\ No newline at end of file
...@@ -32,15 +32,15 @@ export function initSocket(args: SocketInitArgs): Socket | null { ...@@ -32,15 +32,15 @@ export function initSocket(args: SocketInitArgs): Socket | null {
onSocketMessage(args.onMessage); onSocketMessage(args.onMessage);
} }
if (socket) { // if (socket) {
if (socket.connected) { // if (socket.connected) {
args.onConnect?.(socket); // args.onConnect?.(socket);
return socket; // return socket;
} // }
socket.removeAllListeners(); // socket.removeAllListeners();
socket.disconnect(); // socket.disconnect();
socket = null; // socket = null;
} // }
if ($is_run_local()) { if ($is_run_local()) {
console.log('socket.io init:', { console.log('socket.io init:', {
...@@ -68,7 +68,7 @@ export function initSocket(args: SocketInitArgs): Socket | null { ...@@ -68,7 +68,7 @@ export function initSocket(args: SocketInitArgs): Socket | null {
socket.onAny((evt, payload) => { socket.onAny((evt, payload) => {
const data = decode(payload); const data = decode(payload);
if ($is_run_local()) { if ($is_run_local()) {
console.log("recv<============", evt, data); console.log("============>recv", evt, data);
} }
messageHandlers.forEach((handler) => handler(evt, data)); messageHandlers.forEach((handler) => handler(evt, data));
}); });
...@@ -95,7 +95,7 @@ export function sendSocketMessage(cmd: string, data: any = {}): boolean { ...@@ -95,7 +95,7 @@ export function sendSocketMessage(cmd: string, data: any = {}): boolean {
return false; return false;
} }
if ($is_run_local()) { if ($is_run_local()) {
console.log('================>send', cmd, data); console.log('send<================', cmd, data);
} }
socket.send(encode({ cmd, data })); socket.send(encode({ cmd, data }));
return true; return true;
...@@ -114,7 +114,7 @@ export function emitSocket(eventName: string, data?: any): boolean { ...@@ -114,7 +114,7 @@ export function emitSocket(eventName: string, data?: any): boolean {
return true; return true;
} }
export function closeSocket(): void { // export function closeSocket(): void {
socket?.disconnect(); // socket?.disconnect();
socket = null; // socket = null;
} // }
...@@ -2,7 +2,7 @@ import { ref } from 'vue' ...@@ -2,7 +2,7 @@ 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 { initSocket, onSocketMessage, sendSocketMessage } from '@/commons/ws' import { initSocket, onSocketMessage, sendSocketMessage } from '@/commons/ws'
import { $read } from '@/commons/utils' import { $read, $toast, postJson } from '@/commons/utils'
type SocketPayload = { type SocketPayload = {
msg?: string msg?: string
...@@ -76,11 +76,16 @@ export function useGameSocket(options: GameSocketOptions) { ...@@ -76,11 +76,16 @@ export function useGameSocket(options: GameSocketOptions) {
if (state === 0) { if (state === 0) {
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; return;
case 2: case 2: {
// $toast(msg)
alert(msg)
return;
}
case 3: case 3:
alert(msg); // alert(msg);
break; $toast(msg)
return;
} }
} else if (evt == 'submit_score_save') { } else if (evt == 'submit_score_save') {
//提交分数的情况下有问题的情况下,只重试3次 //提交分数的情况下有问题的情况下,只重试3次
...@@ -102,7 +107,8 @@ export function useGameSocket(options: GameSocketOptions) { ...@@ -102,7 +107,8 @@ export function useGameSocket(options: GameSocketOptions) {
} }
if (state === 0) { if (state === 0) {
alert(msg) // alert(msg)
$toast(msg)
return return
} }
...@@ -113,7 +119,9 @@ export function useGameSocket(options: GameSocketOptions) { ...@@ -113,7 +119,9 @@ export function useGameSocket(options: GameSocketOptions) {
} }
case 'room_create_result': { case 'room_create_result': {
if (`game${data}` == options.gameId) { if (`game${data}` == options.gameId) {
location.reload(); // location.reload();
options.onConnect?.();
// debugger
return; return;
} }
const nextPath = getGameHomePath(data) const nextPath = getGameHomePath(data)
...@@ -125,20 +133,14 @@ export function useGameSocket(options: GameSocketOptions) { ...@@ -125,20 +133,14 @@ export function useGameSocket(options: GameSocketOptions) {
} }
case 'room_join_result': { case 'room_join_result': {
if (data != options.gameId) { if (data != options.gameId) {
debugger
return; return;
} }
$toast('您已进入本游戏房间')
userJoinStatus.value = true userJoinStatus.value = true
break break
} }
case 'room_recover_result': { case 'room_recover_result': {
/*{
"gameId": "game6",
"rank": 1,
"reconnect": true,
"remainingSeconds": 56,
"score": 0,
"status": "running"
}*/
//游戏进行中玩家掉线重连时 //游戏进行中玩家掉线重连时
if (data.gameId != options.gameId) { if (data.gameId != options.gameId) {
return; return;
...@@ -160,6 +162,7 @@ export function useGameSocket(options: GameSocketOptions) { ...@@ -160,6 +162,7 @@ export function useGameSocket(options: GameSocketOptions) {
break break
} }
case 'submit_score_save_result': { case 'submit_score_save_result': {
// postJson()
options.onScoreSubmitted?.(true, data) options.onScoreSubmitted?.(true, data)
break break
} }
......
...@@ -6,7 +6,7 @@ import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from ' ...@@ -6,7 +6,7 @@ import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from '
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' import { $getWechat, $toast } from '@/commons/utils.ts'
import { playGame1Music } from '@/commons/music' import { playGame1Music } from '@/commons/music'
type GameView = 'loading' | 'playing' | 'score' type GameView = 'loading' | 'playing' | 'score'
...@@ -212,7 +212,8 @@ if (wechat) { ...@@ -212,7 +212,8 @@ if (wechat) {
stopGameCountdown() stopGameCountdown()
currentView.value = 'loading' currentView.value = 'loading'
setDivDescVisible(false) setDivDescVisible(false)
alert('管理员关闭了该房间') // alert('管理员关闭了该房间')
$toast('管理员关闭了该房间')
}, },
onRoomBack: () => { onRoomBack: () => {
resetToLoadingView() resetToLoadingView()
...@@ -282,14 +283,15 @@ const showGameRuleHandler = () => { ...@@ -282,14 +283,15 @@ const showGameRuleHandler = () => {
background: v-bind('imageUrls.rule') center / cover no-repeat; background: v-bind('imageUrls.rule') center / cover no-repeat;
width: 692px; width: 692px;
height: 797px; height: 797px;
margin-top: -20%;
transform: scale(0.5); transform: scale(0.5);
.rule-title { .rule-title {
text-align: center; text-align: center;
color: white; color: white;
font-weight: bold; font-weight: bold;
font-size: 22pt; font-size: 26pt;
margin-top: 10px; line-height: 50pt;
letter-spacing: 4px; letter-spacing: 4px;
} }
......
...@@ -40,17 +40,18 @@ onMounted(() => { ...@@ -40,17 +40,18 @@ onMounted(() => {
.game-desc { .game-desc {
position: absolute; position: absolute;
top: 50%; top: 40%;
left: var(--stage-viewport-left, 0); left: var(--stage-viewport-left, 0);
display: flex; display: flex;
width: 86px; width: 58px;
height: 256px; height: 158px;
padding-top: 10px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 0 12px 12px 0; border-radius: 0 12px 12px 0;
background: rgba(40, 12, 8, 0.52); background: rgba(40, 12, 8, 0.52);
color: white; color: rgba(255, 255, 255, 0.9);
font-size: 32pt; font-size: 25px;
letter-spacing: 10px; letter-spacing: 10px;
line-height: 1.25; line-height: 1.25;
text-align: center; text-align: center;
......
...@@ -6,7 +6,7 @@ import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from ' ...@@ -6,7 +6,7 @@ import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from '
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' import { $getWechat, $toast } from '@/commons/utils.ts'
const gameId = 'game4'; const gameId = 'game4';
type GameView = 'loading' | 'playing' | 'score' type GameView = 'loading' | 'playing' | 'score'
...@@ -123,7 +123,8 @@ function showLoadingView() { ...@@ -123,7 +123,8 @@ function showLoadingView() {
stopGameCountdown() stopGameCountdown()
currentView.value = 'loading' currentView.value = 'loading'
setDivDescVisible(false) setDivDescVisible(false)
alert('管理员关闭了该房间') // alert('管理员关闭了该房间')
$toast('管理员关闭了该房间')
} }
function resetToLoadingView() { function resetToLoadingView() {
...@@ -266,14 +267,15 @@ onBeforeUnmount(() => { ...@@ -266,14 +267,15 @@ onBeforeUnmount(() => {
background: v-bind('imageUrls.rule') center / cover no-repeat; background: v-bind('imageUrls.rule') center / cover no-repeat;
width: 692px; width: 692px;
height: 797px; height: 797px;
margin-top: -20%;
transform: scale(0.5); transform: scale(0.5);
.rule-title { .rule-title {
text-align: center; text-align: center;
color: white; color: white;
font-weight: bold; font-weight: bold;
font-size: 22pt; font-size: 26pt;
margin-top: 10px; line-height: 50pt;
letter-spacing: 4px; letter-spacing: 4px;
} }
......
...@@ -6,7 +6,7 @@ import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from ' ...@@ -6,7 +6,7 @@ import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from '
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' import { $getWechat, $toast } from '@/commons/utils.ts'
import { playGame1Music } from '@/commons/music' import { playGame1Music } from '@/commons/music'
const gameId = 'game5'; const gameId = 'game5';
...@@ -150,7 +150,8 @@ function showLoadingView() { ...@@ -150,7 +150,8 @@ function showLoadingView() {
stopGameCountdown() stopGameCountdown()
currentView.value = 'loading' currentView.value = 'loading'
setDivDescVisible(false) setDivDescVisible(false)
alert('管理员关闭了该房间') // alert('管理员关闭了该房间')
$toast('管理员关闭了该房间')
} }
function resetToLoadingView() { function resetToLoadingView() {
...@@ -349,14 +350,15 @@ const rankCloseHandler = () => { ...@@ -349,14 +350,15 @@ const rankCloseHandler = () => {
background: v-bind('imageUrls.rule') center / cover no-repeat; background: v-bind('imageUrls.rule') center / cover no-repeat;
width: 692px; width: 692px;
height: 797px; height: 797px;
margin-top: -20%;
transform: scale(0.5); transform: scale(0.5);
.rule-title { .rule-title {
text-align: center; text-align: center;
color: white; color: white;
font-weight: bold; font-weight: bold;
font-size: 22pt; font-size: 26pt;
margin-top: 10px; line-height: 50pt;
letter-spacing: 4px; letter-spacing: 4px;
} }
......
...@@ -42,17 +42,18 @@ onMounted(() => { ...@@ -42,17 +42,18 @@ onMounted(() => {
.game-desc { .game-desc {
position: absolute; position: absolute;
top: 50%; top: 40%;
left: var(--stage-viewport-left, 0); left: var(--stage-viewport-left, 0);
display: flex; display: flex;
width: 86px; width: 58px;
height: 256px; height: 158px;
padding-top: 10px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 0 12px 12px 0; border-radius: 0 12px 12px 0;
background: rgba(40, 12, 8, 0.52); background: rgba(40, 12, 8, 0.52);
color: white; color: rgba(255, 255, 255, 0.9);
font-size: 32pt; font-size: 25px;
letter-spacing: 10px; letter-spacing: 10px;
line-height: 1.25; line-height: 1.25;
text-align: center; text-align: center;
...@@ -93,7 +94,7 @@ onMounted(() => { ...@@ -93,7 +94,7 @@ onMounted(() => {
.img-avatar { .img-avatar {
position: absolute; position: absolute;
bottom: 11%; bottom: 11%;
left: 50%; left: 50%;
/* width: 148px; /* width: 148px;
height: 148px; height: 148px;
...@@ -114,6 +115,7 @@ onMounted(() => { ...@@ -114,6 +115,7 @@ onMounted(() => {
text-align: center; text-align: center;
background: v-bind('imageUrls.contdown') center/cover; background: v-bind('imageUrls.contdown') center/cover;
} }
.txt-nickname { .txt-nickname {
position: absolute; position: absolute;
bottom: 10%; bottom: 10%;
...@@ -146,15 +148,15 @@ onMounted(() => { ...@@ -146,15 +148,15 @@ onMounted(() => {
color: white; color: white;
position: absolute; position: absolute;
z-index: -1; z-index: -1;
top:786px; top: 786px;
left: 50%; left: 50%;
transform: translate(-50%); transform: translate(-50%);
line-height: 118px; line-height: 118px;
text-align: center; text-align: center;
font-weight: 500; font-weight: 500;
font-size: 46px; font-size: 46px;
color: #FFFFFF; color: #FFFFFF;
letter-spacing:5px; letter-spacing: 5px;
background: v-bind('imageUrls.startBg') center/cover; background: v-bind('imageUrls.startBg') center/cover;
} }
......
...@@ -5,7 +5,7 @@ import MobileStage from '@/components/MobileStage.vue' ...@@ -5,7 +5,7 @@ import MobileStage from '@/components/MobileStage.vue'
import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } 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 { $getWechat } from '@/commons/utils.ts' import { $getWechat, $toast } from '@/commons/utils.ts'
import { playGame6Music, stopGame6Music } from '@/commons/music'; import { playGame6Music, stopGame6Music } from '@/commons/music';
type GameView = 'loading' | 'playing' type GameView = 'loading' | 'playing'
...@@ -244,7 +244,7 @@ if (wechat) { ...@@ -244,7 +244,7 @@ if (wechat) {
stopGameCountdown() stopGameCountdown()
currentView.value = 'loading' currentView.value = 'loading'
setDivDescVisible(false) setDivDescVisible(false)
alert('管理员关闭了该房间') $toast('管理员关闭了该房间')
}, },
onRoomBack: () => { onRoomBack: () => {
resetToLoadingView() resetToLoadingView()
...@@ -304,7 +304,7 @@ const rankCloseHandler = () => { ...@@ -304,7 +304,7 @@ const rankCloseHandler = () => {
4. 禁止外挂作弊,违规账号取消成绩与领奖资格;<br /><br /> 4. 禁止外挂作弊,违规账号取消成绩与领奖资格;<br /><br />
祝您游戏愉快! 祝您游戏愉快!
</div> </div>
<div class="rule-close" @click="showGameRuleHandler"></div> <div class="rule-close" @click="showGameRuleHandler"></div>
</div> </div>
</div> </div>
</template> </template>
...@@ -358,14 +358,15 @@ const rankCloseHandler = () => { ...@@ -358,14 +358,15 @@ const rankCloseHandler = () => {
background: v-bind('imageUrls.rule') center / cover no-repeat; background: v-bind('imageUrls.rule') center / cover no-repeat;
width: 692px; width: 692px;
height: 797px; height: 797px;
margin-top: -20%;
transform: scale(0.5); transform: scale(0.5);
.rule-title { .rule-title {
text-align: center; text-align: center;
color: white; color: white;
font-weight: bold; font-weight: bold;
font-size: 22pt; font-size: 26pt;
margin-top: 10px; line-height: 50pt;
letter-spacing: 4px; letter-spacing: 4px;
} }
......
...@@ -77,18 +77,18 @@ onMounted(() => { ...@@ -77,18 +77,18 @@ onMounted(() => {
.game-desc { .game-desc {
position: absolute; position: absolute;
z-index: 9999999999; top: 40%;
top: 50%;
left: var(--stage-viewport-left, 0); left: var(--stage-viewport-left, 0);
display: flex; display: flex;
width: 86px; width: 58px;
height: 256px; height: 158px;
padding-top: 10px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border-radius: 0 12px 12px 0; border-radius: 0 12px 12px 0;
background: rgba(40, 12, 8, 0.52); background: rgba(40, 12, 8, 0.52);
color: white; color: rgba(255, 255, 255, 0.9);
font-size: 32pt; font-size: 25px;
letter-spacing: 10px; letter-spacing: 10px;
line-height: 1.25; line-height: 1.25;
text-align: center; text-align: center;
......
...@@ -23,17 +23,28 @@ export function stopAllMusic() { ...@@ -23,17 +23,28 @@ export function stopAllMusic() {
backgroundMusic = null; backgroundMusic = null;
} }
function playAudioTimes(audio: HTMLAudioElement, times: number) {
let count = 1
audio.currentTime = 0
audio.play()
audio.onended = () => {
if (count >= times) {
audio.onended = null
return
}
count++
audio.currentTime = 0
audio.play()
}
}
function getBackgroundMusic(music: string, force: boolean = true) { function getBackgroundMusic(music: string, force: boolean = true) {
if (force) { // if (force) { //
if (backgroundMusic) { if (backgroundMusic) {
stopApplauseMusic(); stopAllMusic();
stopGame1Music();
// stopGame2Music();
// stopGame3Music();
stopGame4Music();
stopGame5Music();
stopGame6Music();
backgroundMusic = null;
} }
} }
if (!backgroundMusic) { if (!backgroundMusic) {
...@@ -47,13 +58,12 @@ function getBackgroundMusic(music: string, force: boolean = true) { ...@@ -47,13 +58,12 @@ function getBackgroundMusic(music: string, force: boolean = true) {
return backgroundMusic return backgroundMusic
} }
//播放庆祝音乐
export async function playApplauseMusic() { export async function playApplauseMusic() {
const audio = getBackgroundMusic(applause) const audio = getBackgroundMusic(applause)
try { try {
await audio.play() playAudioTimes(audio, 2); //播放两次停止
window.removeEventListener('pointerdown', playApplauseMusic)
window.removeEventListener('keydown', playApplauseMusic)
} catch { } catch {
window.addEventListener('pointerdown', playApplauseMusic, { once: false }) window.addEventListener('pointerdown', playApplauseMusic, { once: false })
window.addEventListener('keydown', playApplauseMusic, { once: false }) window.addEventListener('keydown', playApplauseMusic, { once: false })
......
...@@ -93,4 +93,233 @@ export function $query(q: string | string[]): string | string[] { ...@@ -93,4 +93,233 @@ export function $query(q: string | string[]): string | string[] {
return q.map((key) => params.get(key)).filter((v): v is string => !!v); return q.map((key) => params.get(key)).filter((v): v is string => !!v);
} }
return params.get(q) || ''; return params.get(q) || '';
}
let toastElement: HTMLDivElement | undefined;
let toastTimer: ReturnType<typeof window.setTimeout> | undefined;
export function $toast(message?: string, duration = 2000) {
if (!message) {
return;
}
if (!toastElement) {
toastElement = document.createElement('div');
toastElement.style.cssText = [
'position: fixed',
'left: 50%',
'top: 45%',
'z-index: 99999',
'max-width: 560px',
'padding: 18px 28px',
'border-radius: 18px',
'background: rgba(0, 0, 0, 0.72)',
'color: #fff',
'font-size: 22px',
'line-height: 1.4',
'text-align: center',
'transform: translate(-50%, -40%)',
'opacity: 0',
'pointer-events: none',
'transition: opacity 0.2s ease, transform 0.2s ease',
].join(';');
document.body.appendChild(toastElement);
}
if (toastTimer) {
window.clearTimeout(toastTimer);
toastTimer = undefined;
}
toastElement.textContent = message;
toastElement.style.opacity = '1';
toastElement.style.transform = 'translate(-50%, -50%)';
toastTimer = window.setTimeout(() => {
if (!toastElement) {
return;
}
toastElement.style.opacity = '0';
toastElement.style.transform = 'translate(-50%, -40%)';
toastTimer = undefined;
}, duration);
}
type ConfirmOptions = {
title?: string;
message?: string;
confirmText?: string;
cancelText?: string;
}
let confirmMaskElement: HTMLDivElement | undefined;
let confirmBoxElement: HTMLDivElement | undefined;
let confirmTitleElement: HTMLDivElement | undefined;
let confirmMessageElement: HTMLDivElement | undefined;
let confirmCancelElement: HTMLButtonElement | undefined;
let confirmOkElement: HTMLButtonElement | undefined;
let confirmResolve: ((value: boolean) => void) | undefined;
let confirmHideTimer: ReturnType<typeof window.setTimeout> | undefined;
function closeConfirm(result: boolean) {
if (!confirmMaskElement || !confirmBoxElement) {
confirmResolve?.(result);
confirmResolve = undefined;
return;
}
if (confirmHideTimer) {
window.clearTimeout(confirmHideTimer);
confirmHideTimer = undefined;
}
confirmMaskElement.style.opacity = '0';
confirmBoxElement.style.transform = 'scale(0.96)';
confirmHideTimer = window.setTimeout(() => {
if (confirmMaskElement) {
confirmMaskElement.style.display = 'none';
}
confirmHideTimer = undefined;
}, 200);
confirmResolve?.(result);
confirmResolve = undefined;
}
function ensureConfirmElement() {
if (confirmMaskElement) {
return;
}
confirmMaskElement = document.createElement('div');
confirmMaskElement.style.cssText = [
'position: fixed',
'inset: 0',
'z-index: 99998',
'display: none',
'align-items: center',
'justify-content: center',
'background: rgba(0, 0, 0, 0.45)',
'opacity: 0',
'transition: opacity 0.2s ease',
].join(';');
confirmBoxElement = document.createElement('div');
confirmBoxElement.style.cssText = [
'width: min(560px, calc(100vw - 80px))',
'box-sizing: border-box',
'padding: 34px 32px 28px',
'border-radius: 22px',
'background: #fff',
'color: #333',
'font-size: 24px',
'line-height: 1.45',
'text-align: center',
'box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22)',
'transform: scale(0.96)',
'transition: transform 0.2s ease',
].join(';');
confirmTitleElement = document.createElement('div');
confirmTitleElement.style.cssText = [
'margin-bottom: 16px',
'font-size: 30px',
'font-weight: 700',
'color: #222',
].join(';');
confirmMessageElement = document.createElement('div');
confirmMessageElement.style.cssText = [
'min-height: 34px',
'word-break: break-word',
].join(';');
const buttonWrap = document.createElement('div');
buttonWrap.style.cssText = [
'display: flex',
'gap: 18px',
'margin-top: 30px',
].join(';');
confirmCancelElement = document.createElement('button');
confirmCancelElement.type = 'button';
confirmCancelElement.style.cssText = [
'flex: 1',
'height: 58px',
'border: 1px solid #ddd',
'border-radius: 29px',
'background: #fff',
'color: #666',
'font-size: 24px',
].join(';');
confirmCancelElement.onclick = () => closeConfirm(false);
confirmOkElement = document.createElement('button');
confirmOkElement.type = 'button';
confirmOkElement.style.cssText = [
'flex: 1',
'height: 58px',
'border: 0',
'border-radius: 29px',
'background: #AA0000',
'color: #fff',
'font-size: 24px',
].join(';');
confirmOkElement.onclick = () => closeConfirm(true);
confirmMaskElement.onclick = (event) => {
if (event.target === confirmMaskElement) {
closeConfirm(false);
}
};
buttonWrap.append(confirmCancelElement, confirmOkElement);
confirmBoxElement.append(confirmTitleElement, confirmMessageElement, buttonWrap);
confirmMaskElement.appendChild(confirmBoxElement);
document.body.appendChild(confirmMaskElement);
}
export function $confirm(message?: string, title?: string): Promise<boolean>;
export function $confirm(options?: ConfirmOptions): Promise<boolean>;
export function $confirm(messageOrOptions?: string | ConfirmOptions, title?: string): Promise<boolean> {
const options: ConfirmOptions = typeof messageOrOptions === 'object'
? messageOrOptions
: { message: messageOrOptions, title };
if (!options.message) {
return Promise.resolve(false);
}
ensureConfirmElement();
if (confirmResolve) {
confirmResolve(false);
confirmResolve = undefined;
}
if (confirmHideTimer) {
window.clearTimeout(confirmHideTimer);
confirmHideTimer = undefined;
}
confirmTitleElement!.textContent = options.title ?? '提示';
confirmMessageElement!.textContent = options.message;
confirmCancelElement!.textContent = options.cancelText ?? '取消';
confirmOkElement!.textContent = options.confirmText ?? '确定';
confirmMaskElement!.style.display = 'flex';
window.requestAnimationFrame(() => {
if (!confirmMaskElement || !confirmBoxElement) {
return;
}
confirmMaskElement.style.opacity = '1';
confirmBoxElement.style.transform = 'scale(1)';
});
return new Promise<boolean>((resolve) => {
confirmResolve = resolve;
});
} }
\ No newline at end of file
...@@ -57,7 +57,7 @@ export function initSocket(args: SocketInitArgs): Socket | null { ...@@ -57,7 +57,7 @@ export function initSocket(args: SocketInitArgs): Socket | null {
socket.onAny((evt, payload) => { socket.onAny((evt, payload) => {
const data = decode(payload); const data = decode(payload);
if ($is_run_local()) { if ($is_run_local()) {
console.log("recv<============", evt, data); console.log("============>recv", evt, data);
} }
messageHandlers.forEach((handler) => handler(evt, data)); messageHandlers.forEach((handler) => handler(evt, data));
}); });
...@@ -78,7 +78,7 @@ export function sendSocketMessage(cmd: string, data: any = {}): boolean { ...@@ -78,7 +78,7 @@ export function sendSocketMessage(cmd: string, data: any = {}): boolean {
return false; return false;
} }
if ($is_run_local()) { if ($is_run_local()) {
console.log('================>send', cmd, data); console.log('send<================', cmd, data);
} }
socket.send(encode({ cmd, data })); socket.send(encode({ cmd, data }));
return true; return true;
......
import CryptoJS from 'crypto-js' import CryptoJS from 'crypto-js'
import { onBeforeUnmount, onMounted } from 'vue' import { onBeforeUnmount, onMounted } from 'vue'
import { $read_socket_storge, $remove_socket_storage } from '@/commons/utils' import { $read_socket_storge, $remove_socket_storage, $toast } from '@/commons/utils'
import { initSocket, onSocketMessage, sendSocketMessage } from '@/commons/ws' import { initSocket, onSocketMessage, sendSocketMessage } from '@/commons/ws'
import router from '@/router' import router from '@/router'
...@@ -64,21 +64,18 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) { ...@@ -64,21 +64,18 @@ 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 (state === 0) { if (state === 0) {
// if (evt === 'game_start_result' && data === 1) {
// return
// }
if (evt == 'login_result') { if (evt == 'login_result') {
alert(msg) // alert(msg)
// options.onRelogin?.() // options.onRelogin?.()
socket?.close(); $toast(msg);
$remove_socket_storage(); $remove_socket_storage();
await router.replace(`/login`); await router.replace(`/login`);
location.href = location.href; location.href = location.href;
return; return;
} }
else if (evt == 'new_admin_result') { else if (evt == 'new_admin_result') {
alert(msg); // alert(msg);
socket?.close(); $toast(msg);
$remove_socket_storage(); $remove_socket_storage();
await router.replace(`/login`); await router.replace(`/login`);
location.href = location.href; location.href = location.href;
......
<script setup lang="ts"> <script setup lang="ts">
import { cssAssetUrl } from '@/commons/assets'; import { cssAssetUrl } from '@/commons/assets';
import { $save } from '@/commons/utils.ts'; import { $save, $toast } from '@/commons/utils.ts';
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
...@@ -9,9 +9,9 @@ const username = ref('') ...@@ -9,9 +9,9 @@ const username = ref('')
const password = ref('') const password = ref('')
const imageUrls = { 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'),
} }
async function login() { async function login() {
...@@ -25,10 +25,11 @@ async function login() { ...@@ -25,10 +25,11 @@ async function login() {
}) })
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') $toast(data.msg)
// throw new Error(data.msg || 'Login failed') //alert(data.msg || 'Login failed')
// } // throw new Error(data.msg || 'Login failed')
}
return data return data
} }
...@@ -36,13 +37,15 @@ const loginHandler = async () => { ...@@ -36,13 +37,15 @@ const loginHandler = async () => {
const { state, msg, data } = await login() const { state, msg, data } = await login()
if (state === 0) { if (state === 0) {
alert(msg) // alert(msg)
$toast(msg)
return return
} }
for (let i in data) { for (let i in data) {
$save(i, data[i]); $save(i, data[i]);
} }
alert('登录成功') // alert('登录成功')
$toast('登录成功')
await router.replace(`/main`); await router.replace(`/main`);
} }
...@@ -77,34 +80,33 @@ onMounted(() => { ...@@ -77,34 +80,33 @@ onMounted(() => {
</template> </template>
<style scoped> <style scoped>
.bg { .bg {
width: 1920px; width: 1920px;
height: 1080px; height: 1080px;
background: v-bind('imageUrls.bg1') center / cover no-repeat; background: v-bind('imageUrls.bg1') center / cover no-repeat;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
.img-logo {
background: v-bind('imageUrls.logo');
background-size: cover;
width: 428px;
height: 77px;
position: absolute;
left: 37px;
top: 82px;
}
.img-logo { .img-title1 {
background: v-bind('imageUrls.logo'); background: v-bind('imageUrls.title1') center / cover no-repeat;
background-size: cover; width: 1024px;
width: 428px; height: 143px;
height: 77px; margin: 0 auto;
position: absolute; position: relative;
left: 37px; top: 50px;
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 { .login-page {
......
...@@ -7,7 +7,7 @@ import Rank2 from './views/Rank2View.vue' ...@@ -7,7 +7,7 @@ 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 { playApplauseMusic, playGame1Music, stopAllMusic, stopGame1Music } from '@/commons/music' import { playApplauseMusic, playGame1Music, stopAllMusic, stopGame1Music } from '@/commons/music'
import { $remove_socket_storage } from '@/commons/utils.ts' import { $confirm, $remove_socket_storage, $toast } from '@/commons/utils.ts'
const router = useRouter() const router = useRouter()
...@@ -155,7 +155,8 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({ ...@@ -155,7 +155,8 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
const startGameWithMusic = () => { const startGameWithMusic = () => {
if (playerCount.value == 0) { if (playerCount.value == 0) {
alert('房间中没有玩家') // alert('房间中没有玩家')
$toast('房间中没有玩家');
return; return;
} }
startGame() startGame()
...@@ -171,10 +172,16 @@ const nextRoundWithMusic = () => { ...@@ -171,10 +172,16 @@ const nextRoundWithMusic = () => {
nextRound() nextRound()
} }
const backHandler = () => { const backHandler = async () => {
if (confirm('是否关闭当前房间回到首页')) { const ok = await $confirm({
title: '提示',
message: '是否关闭当前房间回到首页',
confirmText: '确定',
cancelText: '取消',
})
if (ok) {
closeRoom(); closeRoom();
stopGame1Music() stopAllMusic()
router.replace('/main'); router.replace('/main');
} }
} }
...@@ -191,9 +198,9 @@ onUnmounted(() => { ...@@ -191,9 +198,9 @@ onUnmounted(() => {
watch(screen, (value) => { watch(screen, (value) => {
// console.log(value); // console.log(value);
if(value == 'rank2'){ if (value == 'rank2') {
playApplauseMusic(); playApplauseMusic();
}else if(value == 'loading'){ } else if (value == 'loading') {
playGame1Music(); playGame1Music();
} }
}) })
......
...@@ -152,11 +152,10 @@ const renderAvatar = (item: any | null) => { ...@@ -152,11 +152,10 @@ const renderAvatar = (item: any | null) => {
.podium { .podium {
background: v-bind('imageUrls.podium') no-repeat center; background: v-bind('imageUrls.podium') no-repeat center;
width: 1347px; width: 1920px;
height: 269px; height: 740px;
position: absolute; position: absolute;
top: 427px; bottom: 0;
left: 287px;
.gu-burst { .gu-burst {
width: 200px; width: 200px;
...@@ -174,6 +173,8 @@ const renderAvatar = (item: any | null) => { ...@@ -174,6 +173,8 @@ const renderAvatar = (item: any | null) => {
.rank-no1 { .rank-no1 {
position: relative; position: relative;
left: 308px;
top: 150px;
.gu-burst { .gu-burst {
left: 580px; left: 580px;
...@@ -207,7 +208,8 @@ const renderAvatar = (item: any | null) => { ...@@ -207,7 +208,8 @@ const renderAvatar = (item: any | null) => {
.rank-no2 { .rank-no2 {
position: relative; position: relative;
left: 308px;
top: 150px;
.gu-burst { .gu-burst {
left: 140px; left: 140px;
top: -170px; top: -170px;
...@@ -232,7 +234,7 @@ const renderAvatar = (item: any | null) => { ...@@ -232,7 +234,7 @@ const renderAvatar = (item: any | null) => {
text-align: center; text-align: center;
line-height: 46px; line-height: 46px;
position: absolute; position: absolute;
left: 157px; left: 165px;
top: 2px; top: 2px;
z-index: 1; z-index: 1;
border-radius: 23px; border-radius: 23px;
...@@ -241,7 +243,8 @@ const renderAvatar = (item: any | null) => { ...@@ -241,7 +243,8 @@ const renderAvatar = (item: any | null) => {
.rank-no3 { .rank-no3 {
position: relative; position: relative;
left: 308px;
top: 150px;
.gu-burst { .gu-burst {
left: 1020px; left: 1020px;
top: -170px; top: -170px;
...@@ -280,8 +283,8 @@ const renderAvatar = (item: any | null) => { ...@@ -280,8 +283,8 @@ const renderAvatar = (item: any | null) => {
height: 238px; height: 238px;
background: v-bind('imageUrls.vip'); background: v-bind('imageUrls.vip');
position: absolute; position: absolute;
left: 171px; left: 480px;
top: -200px; top: -50px;
} }
.rank-container { .rank-container {
...@@ -324,19 +327,21 @@ const renderAvatar = (item: any | null) => { ...@@ -324,19 +327,21 @@ const renderAvatar = (item: any | null) => {
position: absolute; position: absolute;
inset: 0; inset: 0;
border-radius: inherit; border-radius: inherit;
background: v-bind('imageUrls.avatarAnimation'); transform-origin: center center;
background: v-bind('imageUrls.avatarAnimation') center / cover no-repeat;
animation: avatar-rotate 10s linear infinite; animation: avatar-rotate 10s linear infinite;
} }
.img-avatar { .img-avatar {
position: relative; position: absolute;
top: 6px; left: 50%;
top: 50%;
z-index: 1; z-index: 1;
margin: 0 auto;
width: 60px; width: 60px;
height: 60px; height: 60px;
border-radius: 30px; border-radius: 30px;
background: v-bind('imageUrls.avatar'); transform: translate(-50%, -50%);
background: v-bind('imageUrls.avatar') center / cover no-repeat;
} }
.txt-nickname { .txt-nickname {
......
...@@ -8,7 +8,7 @@ import { useAdminGameSocket } from "@/composables/useAdminGameSocket"; ...@@ -8,7 +8,7 @@ import { useAdminGameSocket } from "@/composables/useAdminGameSocket";
import { onSocketMessage } from "@/commons/ws"; import { onSocketMessage } from "@/commons/ws";
import type Player from "@/commons/player.ts"; import type Player from "@/commons/player.ts";
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { $remove_socket_storage } from "@/commons/utils.ts"; import { $confirm, $remove_socket_storage, $toast } from "@/commons/utils.ts";
const router = useRouter() const router = useRouter()
...@@ -194,7 +194,8 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({ ...@@ -194,7 +194,8 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
const startGameWithMusic = () => { const startGameWithMusic = () => {
if (playerCount.value == 0) { if (playerCount.value == 0) {
alert('房间中没有玩家') // alert('房间中没有玩家')
$toast('房间中没有玩家');
return; return;
} }
startGame() startGame()
...@@ -210,10 +211,16 @@ const nextRoundWithMusic = () => { ...@@ -210,10 +211,16 @@ const nextRoundWithMusic = () => {
nextRound() nextRound()
} }
const backHandler = () => { const backHandler = async() => {
if (confirm('是否关闭当前房间回到首页')) { const ok = await $confirm({
title: '提示',
message: '是否关闭当前房间回到首页',
confirmText: '确定',
cancelText: '取消',
})
if (ok) {
closeRoom(); closeRoom();
stopGame4Music() stopAllMusic()
router.replace('/main'); router.replace('/main');
} }
} }
......
...@@ -7,7 +7,7 @@ import Rank2 from './views/Rank2View.vue' ...@@ -7,7 +7,7 @@ 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 { playApplauseMusic, playGame5Music, stopAllMusic, stopGame5Music } from '@/commons/music' import { playApplauseMusic, playGame5Music, stopAllMusic, stopGame5Music } from '@/commons/music'
import { $remove_socket_storage } from '@/commons/utils.ts' import { $confirm, $remove_socket_storage, $toast } from '@/commons/utils.ts'
const router = useRouter() const router = useRouter()
...@@ -163,7 +163,8 @@ const { startGame, createRoom, closeRoom, backRoom } = useAdminGameSocket({ ...@@ -163,7 +163,8 @@ const { startGame, createRoom, closeRoom, backRoom } = useAdminGameSocket({
const startGameWithMusic = () => { const startGameWithMusic = () => {
if (playerCount.value == 0) { if (playerCount.value == 0) {
alert('房间中没有玩家') // alert('房间中没有玩家')
$toast('房间中没有玩家');
return; return;
} }
playGame5Music() playGame5Music()
...@@ -191,10 +192,16 @@ onUnmounted(() => { ...@@ -191,10 +192,16 @@ onUnmounted(() => {
window.removeEventListener('beforeunload', confirmRefresh) window.removeEventListener('beforeunload', confirmRefresh)
}) })
const backHandler = () => { const backHandler = async() => {
if (confirm('是否关闭当前房间回到首页')) { const ok = await $confirm({
title: '提示',
message: '是否关闭当前房间回到首页',
confirmText: '确定',
cancelText: '取消',
})
if (ok) {
closeRoom(); closeRoom();
stopGame5Music() stopAllMusic()
router.replace('/main'); router.replace('/main');
} }
} }
......
...@@ -7,7 +7,7 @@ import Rank2 from './views/Rank2View.vue' ...@@ -7,7 +7,7 @@ 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 { playApplauseMusic, playGame6Music, stopAllMusic, stopGame6Music } from '@/commons/music' import { playApplauseMusic, playGame6Music, stopAllMusic, stopGame6Music } from '@/commons/music'
import { $remove_socket_storage } from '@/commons/utils.ts' import { $confirm, $remove_socket_storage, $toast } from '@/commons/utils.ts'
const router = useRouter() const router = useRouter()
...@@ -153,7 +153,8 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({ ...@@ -153,7 +153,8 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
const startGameWithMusic = () => { const startGameWithMusic = () => {
if (playerCount.value == 0) { if (playerCount.value == 0) {
alert('房间中没有玩家') // alert('房间中没有玩家')
$toast('房间中没有玩家');
return; return;
} }
startGame() startGame()
...@@ -169,10 +170,16 @@ const nextRoundWithMusic = () => { ...@@ -169,10 +170,16 @@ const nextRoundWithMusic = () => {
nextRound() nextRound()
} }
const backHandler = () => { const backHandler = async() => {
if (confirm('是否关闭当前房间回到首页')) { const ok = await $confirm({
title: '提示',
message: '是否关闭当前房间回到首页',
confirmText: '确定',
cancelText: '取消',
})
if (ok) {
closeRoom(); closeRoom();
stopGame6Music() stopAllMusic()
router.replace('/main'); router.replace('/main');
} }
} }
......
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