Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
fc-minigame
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
陈冲
fc-minigame
Commits
8a5923a5
Commit
8a5923a5
authored
Jun 02, 2026
by
陈冲
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
更新h5代码
parent
948fdad3
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
397 additions
and
438 deletions
+397
-438
Cargo.lock
backend/Cargo.lock
+26
-26
Cargo.toml
backend/Cargo.toml
+2
-2
game.sql
backend/deploy/game.sql
+4
-6
game_mysql.sql
backend/deploy/game_mysql.sql
+2
-4
game.rs
backend/src/db/models/game.rs
+7
-11
socket_io.rs
backend/src/protocols/socket_io.rs
+65
-53
utils.ts
frontend-h5/src/commons/utils.ts
+26
-32
ws.ts
frontend-h5/src/commons/ws.ts
+21
-1
MobileStage.vue
frontend-h5/src/components/MobileStage.vue
+3
-3
useGameSocket.ts
frontend-h5/src/composables/useGameSocket.ts
+51
-36
Login.vue
frontend-h5/src/pages/Login.vue
+0
-123
Game1.vue
frontend-h5/src/pages/game1/Game1.vue
+68
-32
LoadingView.vue
frontend-h5/src/pages/game1/views/LoadingView.vue
+16
-72
ScoreView.vue
frontend-h5/src/pages/game1/views/ScoreView.vue
+2
-2
index.ts
frontend-h5/src/router/index.ts
+8
-14
vite.config.ts
frontend-h5/vite.config.ts
+1
-1
game1.mp3
frontend-large/src/assets/images/game1.mp3
+0
-0
qrcode.png
frontend-large/src/assets/images/game1/qrcode.png
+0
-0
music.ts
frontend-large/src/commons/music.ts
+34
-0
utils.ts
frontend-large/src/commons/utils.ts
+12
-2
ws.ts
frontend-large/src/commons/ws.ts
+3
-1
useAdminGameSocket.ts
frontend-large/src/composables/useAdminGameSocket.ts
+4
-4
Login.vue
frontend-large/src/pages/Login.vue
+8
-2
Game1.vue
frontend-large/src/pages/game1/Game1.vue
+15
-3
LoadingView.vue
frontend-large/src/pages/game1/views/LoadingView.vue
+2
-2
Rank2View.vue
frontend-large/src/pages/game1/views/Rank2View.vue
+14
-4
vite.config.ts
frontend-large/vite.config.ts
+3
-2
No files found.
backend/Cargo.lock
View file @
8a5923a5
...
...
@@ -1031,6 +1031,32 @@ dependencies = [
]
[[package]]
name = "hddpServer"
version = "0.1.0"
dependencies = [
"aes",
"async-trait",
"base64",
"bytes",
"cbc",
"chrono",
"dashmap",
"lazy_static",
"md-5",
"rand 0.10.0",
"rmp-serde",
"rust-embed",
"salvo",
"serde",
"serde_json",
"socketioxide",
"sqlx",
"tokio",
"tower",
"tower-http",
]
[[package]]
name = "headers"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
...
...
@@ -1611,32 +1637,6 @@ dependencies = [
]
[[package]]
name = "minigame"
version = "0.1.0"
dependencies = [
"aes",
"async-trait",
"base64",
"bytes",
"cbc",
"chrono",
"dashmap",
"lazy_static",
"md-5",
"rand 0.10.0",
"rmp-serde",
"rust-embed",
"salvo",
"serde",
"serde_json",
"socketioxide",
"sqlx",
"tokio",
"tower",
"tower-http",
]
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
...
...
backend/Cargo.toml
View file @
8a5923a5
[package]
name
=
"
minigame
"
name
=
"
hddpServer
"
version
=
"0.1.0"
edition
=
"2024"
...
...
@@ -27,7 +27,7 @@ cbc = { version = "0.1.2", features = ["alloc"] }
md5
=
{
version
=
"0.10.6"
,
package
=
"md-5"
}
[[bin]]
name
=
"
minigame
"
name
=
"
hddpServer
"
[target.x86_64-unknown-linux-musl]
linker
=
"rust-lld"
...
...
backend/deploy/game.sql
View file @
8a5923a5
drop
table
if
exists
tb_game
;
create
table
tb_game
(
event_date
date
,
-- 活动日期
wechat_id
varchar
(
50
),
-- 微信id
wechat_id
text
,
-- 微信id
nickname
varchar
(
50
),
-- 昵称
avatar
varchar
(
200
),
-- 头像
round_num
int
NOT
NULL
CHECK
(
round_num
between
1
and
3
),
-- 比赛场次
avatar
text
,
-- 头像
game_rank
int
,
-- 排名
item_num
int
not
null
check
(
item_num
between
1
and
6
),
-- 游戏项目(1到6)
score
int
not
null
default
0
check
(
score
>=
0
),
-- 得分
create_date
timestamptz
,
-- 创建时间
unique
(
event_date
,
wechat_id
,
round_num
,
item_num
)
create_date
timestamptz
-- 创建时间
);
SELECT
*
FROM
tb_game
...
...
backend/deploy/game_mysql.sql
View file @
8a5923a5
DROP
TABLE
IF
EXISTS
tb_game
;
CREATE
TABLE
tb_game
(
event_date
DATE
,
wechat_id
VARCHAR
(
50
),
nickname
VARCHAR
(
50
),
avatar
VARCHAR
(
200
),
round_num
INT
NOT
NULL
,
game_rank
INT
NOT
NULL
,
item_num
INT
NOT
NULL
,
score
INT
NOT
NULL
DEFAULT
0
,
create_date
DATETIME
,
UNIQUE
KEY
uk_tb_game_player_item
(
event_date
,
wechat_id
,
round_num
,
item_num
)
create_date
DATETIME
);
DROP
TABLE
IF
EXISTS
tb_log
;
...
...
backend/src/db/models/game.rs
View file @
8a5923a5
...
...
@@ -4,15 +4,13 @@ use serde_json::json;
use
crate
::
db
::
models
::
log
::
Log
;
pub
struct
Game
{
/// 活动日期
pub
event_date
:
NaiveDate
,
pub
wechat_id
:
String
,
pub
nickname
:
Option
<
String
>
,
pub
avatar
:
Option
<
String
>
,
/// 比赛场次
pub
round_num
:
i32
,
pub
nickname
:
String
,
pub
avatar
:
String
,
/// 游戏项目(1到6)
pub
item_num
:
i32
,
/// 排序
pub
game_rank
:
i32
,
/// 得分
pub
score
:
i32
,
/// 创建时间
...
...
@@ -22,14 +20,13 @@ pub struct Game {
impl
Game
{
pub
async
fn
insert
(
&
self
)
->
bool
{
let
res
=
crate
::
db
::
db_query
(
r
#
"insert into tb_game(
event_date,wechat_id,nickname,avatar,round_num,item_num,score,create_date) values(?::date,
?,?,?,?,?,?,now());"
#
,
r
#
"insert into tb_game(
wechat_id,nickname,avatar,item_num,game_rank,score,create_date) values(
?,?,?,?,?,?,now());"
#
,
vec!
[
json!
(
&
self
.event_date
),
json!
(
&
self
.wechat_id
),
json!
(
&
self
.nickname
),
json!
(
&
self
.avatar
),
json!
(
&
self
.round_num
),
json!
(
&
self
.item_num
),
json!
(
&
self
.game_rank
),
json!
(
&
self
.score
),
],
)
...
...
@@ -46,12 +43,11 @@ impl Game {
id
:
0
,
msg
:
err
,
params
:
json!
({
"event_date"
:
self
.event_date
,
"wechat_id"
:
self
.wechat_id
,
"nickname"
:
self
.nickname
,
"avatar"
:
self
.avatar
,
"round_num"
:
self
.round_num
,
"item_num"
:
self
.item_num
,
"game_rank"
:
self
.game_rank
,
"score"
:
self
.score
,
}),
create_date
:
None
,
...
...
backend/src/protocols/socket_io.rs
View file @
8a5923a5
This diff is collapsed.
Click to expand it.
frontend-h5/src/commons/utils.ts
View file @
8a5923a5
import
SHA256
from
'crypto-js/sha256'
type
StorageRecord
=
Record
<
string
,
unknown
>
;
function
isStorageRecord
(
value
:
unknown
):
value
is
StorageRecord
{
...
...
@@ -54,37 +56,6 @@ export function $read<T>(key: string | StorageRecord, defaultValue?: T): T | Sto
}
}
export
function
$goto_login
(
evt
:
string
,
router
:
any
,
path
:
string
):
boolean
{
if
(
evt
==
"login_result"
)
{
$remove_socket_storage
();
router
.
replace
(
path
).
then
(()
=>
{
});
return
true
;
}
return
false
;
}
export
function
$remove_socket_storage
():
void
{
localStorage
.
removeItem
(
'socket_path'
);
localStorage
.
removeItem
(
'client_ns'
);
localStorage
.
removeItem
(
'wechat_id'
);
localStorage
.
removeItem
(
'nickname'
);
localStorage
.
removeItem
(
'telephone'
);
localStorage
.
removeItem
(
'avatar'
);
}
export
function
$read_socket_storge
():
any
{
const
domain
=
$read
(
'domain'
,
''
);
const
socket_path
=
$read
(
'socket_path'
,
''
);
const
client_ns
=
$read
(
'client_ns'
,
''
)
const
wechat_id
=
$read
(
'wechat_id'
,
''
);
const
nickname
=
$read
(
'nickname'
,
''
);
const
telephone
=
$read
(
'telephone'
,
''
);
const
avatar
=
$read
(
'avatar'
,
''
);
return
{
domain
,
socket_path
,
client_ns
,
wechat_id
,
nickname
,
telephone
,
avatar
}
}
export
function
$format_str
(
str
:
string
,
num
:
number
)
{
if
(
str
.
length
<=
num
-
2
)
{
return
str
;
...
...
@@ -93,8 +64,30 @@ export function $format_str(str: string, num: number) {
}
export
function
$is_run_local
():
boolean
{
if
(
/localhost|192.168/gim
.
test
(
location
.
href
))
{
if
(
/
https
?
:
\/\/
localhost|192.168/gim
.
test
(
location
.
href
))
{
return
true
;
}
return
false
;
}
function
$query
<
T
extends
string
|
string
[]
>
(
q
:
T
):
T
{
const
query
=
location
.
href
.
match
(
/
\?([^
#
]
*
)
/
)?.[
1
]?.
replace
(
/
\/
$/
,
''
);
const
params
=
new
URLSearchParams
(
query
);
if
(
Array
.
isArray
(
q
))
{
return
q
.
map
((
key
)
=>
params
.
get
(
key
)).
filter
((
v
):
v
is
string
=>
!!
v
)
as
T
;
}
return
params
.
get
(
q
)
as
T
;
}
export
function
$getWechat
():
any
|
null
{
const
q
=
$query
([
'token'
,
'nickname'
,
'avatar'
]);
if
(
q
&&
q
.
length
==
3
)
{
return
{
token
:
SHA256
(
q
[
0
]).
toString
(),
//原token很长,这里压缩下,压缩后不能还原,最后提交成绩时要提交原始token
token_origin
:
q
[
0
],
nickname
:
q
[
1
],
avatar
:
q
[
2
],
};
}
return
null
;
}
\ No newline at end of file
frontend-h5/src/commons/ws.ts
View file @
8a5923a5
...
...
@@ -35,9 +35,20 @@ export function initSocket(args: SocketInitArgs): Socket | null {
if
(
socket
)
{
if
(
socket
.
connected
)
{
args
.
onConnect
?.(
socket
);
}
return
socket
;
}
socket
.
removeAllListeners
();
socket
.
disconnect
();
socket
=
null
;
}
if
(
$is_run_local
())
{
console
.
log
(
'socket.io init:'
,
{
namespace
:
args
.
client_ns
,
path
:
args
.
socket_path
,
auth
:
args
.
auth
,
});
}
socket
=
io
(
args
.
client_ns
,
{
path
:
args
.
socket_path
,
...
...
@@ -49,6 +60,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
});
socket
.
on
(
'connect'
,
()
=>
{
if
(
$is_run_local
())
{
console
.
log
(
'socket.io connected:'
,
socket
?.
id
);
}
args
.
onConnect
?.(
socket
!
);
});
socket
.
onAny
((
evt
,
payload
)
=>
{
...
...
@@ -60,6 +74,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
});
socket
.
on
(
'disconnect'
,
(
reason
)
=>
{
if
(
$is_run_local
())
{
console
.
log
(
'socket.io disconnect:'
,
reason
);
}
args
.
onDisconnect
?.(
reason
);
});
...
...
@@ -72,6 +89,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
export
function
sendSocketMessage
(
cmd
:
string
,
data
:
any
=
{}):
boolean
{
if
(
!
socket
?.
connected
)
{
if
(
$is_run_local
())
{
console
.
log
(
'socket.io send blocked:'
,
cmd
,
data
);
}
return
false
;
}
...
...
frontend-h5/src/components/MobileStage.vue
View file @
8a5923a5
...
...
@@ -144,11 +144,11 @@ defineExpose({
<main
class=
"mobile-stage-viewport"
>
<div
class=
"mobile-stage-background"
:style=
"backgroundStyle"
/>
<div
class=
"mobile-stage"
:style=
"stageStyle"
>
<slot
/>
</div>
<div
v-if=
"countdownVisible"
class=
"countdown-modal"
>
<div
:key=
"countdownKey"
class=
"countdown-number"
>
{{
countdown
}}
</div>
</div>
<slot
/>
</div>
</main>
</
template
>
...
...
@@ -187,7 +187,7 @@ defineExpose({
.countdown-number
{
color
:
white
;
font-size
:
3
00pt
;
font-size
:
2
00pt
;
font-weight
:
800
;
line-height
:
1
;
text-shadow
:
0
18px
38px
rgba
(
0
,
0
,
0
,
0.25
);
...
...
frontend-h5/src/composables/useGameSocket.ts
View file @
8a5923a5
import
{
onBeforeUnmount
,
onMounted
,
ref
}
from
'vue'
import
{
ref
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
type
{
Socket
}
from
'socket.io-client'
import
{
$read_socket_storge
}
from
'@/commons/utils'
import
{
initSocket
,
onSocketMessage
,
sendSocketMessage
}
from
'@/commons/ws'
import
{
$read
}
from
'@/commons/utils'
type
SocketPayload
=
{
msg
?:
string
...
...
@@ -11,9 +11,12 @@ type SocketPayload = {
}
type
GameSocketOptions
=
{
gameId
:
string
gameId
:
string
,
auth
:
any
,
onConnect
?:
()
=>
void
,
onGameStart
?:
(
data
:
any
)
=>
void
onScoreSubmitted
?:
(
data
:
any
)
=>
void
onScoreSubmitted
?:
(
is_save
:
boolean
,
data
:
any
)
=>
void
onRescoreSubmitted
?:
(
recount
:
number
)
=>
void
onRoomClosed
?:
(
data
:
any
)
=>
void
onEvent
?:
(
evt
:
string
,
payload
:
SocketPayload
)
=>
void
}
...
...
@@ -36,45 +39,55 @@ function getGameHomePath(data: any) {
return
`/game
${
gameIndex
}
`
}
export
function
joinSharedRoom
(
nickname
:
string
,
token
:
string
,
avatar
:
string
)
{
return
sendSocketMessage
(
'room_join'
,
{
nickname
:
nickname
,
token
,
avatar
})
}
export
function
sendGameMessage
(
cmd
:
string
,
data
:
any
=
{})
{
return
sendSocketMessage
(
cmd
,
data
)
}
export
const
userJoinStatus
=
ref
(
false
)
export
function
useGameSocket
(
options
:
GameSocketOptions
)
{
const
router
=
useRouter
()
const
userJoinStatus
=
ref
(
false
)
let
socket
:
Socket
|
null
=
null
let
offSocketMessage
:
(()
=>
void
)
|
undefined
function
joinSharedRoom
()
{
const
{
nickname
,
telephone
,
avatar
}
=
$read_socket_storge
()
return
sendSocketMessage
(
'room_join'
,
{
nickname
,
telephone
,
avatar
})
}
function
sendGameMessage
(
cmd
:
string
,
data
:
any
=
{})
{
return
sendSocketMessage
(
cmd
,
data
)
}
onMounted
(()
=>
{
const
{
domain
,
socket_path
,
client_ns
,
wechat_id
}
=
$read_socket_storge
()
const
domain
=
$read
(
'domain'
,
''
);
let
resubmitcount
=
0
;
socket
=
initSocket
({
socket_path
:
`
${
domain
}${
socket_path
}
`
,
client_ns
,
auth
:
{
userid
:
wechat_id
,
},
onConnect
:
()
=>
{
window
.
setTimeout
(
joinSharedRoom
,
200
)
socket_path
:
'/socket.io'
,
client_ns
:
`
${
domain
}
/ws`
,
auth
:
options
.
auth
,
onConnect
:
(
socket
)
=>
{
options
.
onConnect
?.();
},
onDisconnect
:
(
_reason
:
string
)
=>
{
},
onError
:
(
_error
:
any
)
=>
{
},
onError
:
(
error
:
any
)
=>
{
console
.
error
(
'socket.io connect_error:'
,
error
)
},
})
offSocketMessage
=
onSocketMessage
((
evt
,
payload
:
SocketPayload
)
=>
{
const
{
msg
,
state
,
data
}
=
payload
if
(
state
===
0
&&
evt
===
'room_join_result'
)
{
if
(
data
===
1
)
{
userJoinStatus
.
value
=
true
if
(
state
===
0
)
{
if
(
evt
===
'room_join_result'
)
{
switch
(
data
)
{
case
1
:
userJoinStatus
.
value
=
true
;
break
;
case
3
:
alert
(
msg
);
break
;
}
return
}
else
if
(
evt
==
'submit_score_save'
)
{
//提交分数的情况下有问题的情况下,只重试3次
if
(
resubmitcount
>=
3
)
{
return
;
}
setTimeout
(()
=>
{
resubmitcount
++
;
options
.
onRescoreSubmitted
?.(
resubmitcount
);
},
200
);
}
return
;
}
if
(
state
===
0
)
{
...
...
@@ -102,7 +115,11 @@ export function useGameSocket(options: GameSocketOptions) {
break
}
case
'submit_score_result'
:
{
options
.
onScoreSubmitted
?.(
data
)
options
.
onScoreSubmitted
?.(
false
,
data
)
break
}
case
'submit_score_save_result'
:
{
options
.
onScoreSubmitted
?.(
true
,
data
)
break
}
case
'room_close'
:
{
...
...
@@ -114,16 +131,14 @@ export function useGameSocket(options: GameSocketOptions) {
options
.
onEvent
?.(
evt
,
payload
)
})
})
onBeforeUnmount
(()
=>
{
offSocketMessage
?.()
})
//
onBeforeUnmount(() => {
//
offSocketMessage?.()
//
})
return
{
socket
,
userJoinStatus
,
joinSharedRoom
,
sendGameMessage
,
offSocketMessage
}
}
frontend-h5/src/pages/Login.vue
deleted
100644 → 0
View file @
948fdad3
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
MobileStage
from
'@/components/MobileStage.vue'
import
{
$save
}
from
'@/commons/utils'
const
router
=
useRouter
()
const
nickname
=
ref
(
''
)
async
function
getConn
()
{
let
domain
=
localStorage
.
getItem
(
'domain'
);
const
res
=
await
fetch
(
`
${
domain
}
/manager/getConn`
,
{
method
:
'POST'
,
headers
:
{
'Content-Type'
:
'application/json'
,
},
body
:
JSON
.
stringify
({}),
})
const
data
=
await
res
.
json
()
if
(
!
res
.
ok
||
data
.
state
!==
1
)
{
alert
(
data
.
msg
||
'Login failed'
)
throw
new
Error
(
data
.
msg
||
'Login failed'
)
}
return
data
}
const
loginHandler
=
async
()
=>
{
const
{
state
,
msg
,
data
}
=
await
getConn
()
if
(
state
===
0
)
{
alert
(
msg
)
return
}
for
(
let
i
in
data
)
{
$save
(
i
,
data
[
i
]);
}
$save
({
wechat_id
:
nickname
.
value
,
nickname
:
nickname
.
value
,
telephone
:
'13111223344'
,
avatar
:
'13111223344'
,
});
await
router
.
replace
(
'/game1'
)
}
</
script
>
<
template
>
<MobileStage>
<section
class=
"h5-page login-page"
>
<div
class=
"brand"
>
<span
class=
"brand-mark"
>
H5
</span>
<div>
<h1
class=
"h5-title"
>
小游戏
</h1>
<p
class=
"h5-subtitle"
>
小屏互动端
</p>
</div>
</div>
<form
class=
"login-form h5-panel"
>
<label
class=
"field"
>
<span>
昵称
</span>
<input
v-model=
"nickname"
maxlength=
"16"
placeholder=
"请输入昵称"
autocomplete=
"nickname"
>
</label>
<input
class=
"h5-button"
@
click=
"loginHandler"
type=
"button"
value=
"进入游戏"
>
</form>
</section>
</MobileStage>
</
template
>
<
style
scoped
>
.login-page
{
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
gap
:
32px
;
}
.brand
{
display
:
flex
;
align-items
:
center
;
gap
:
14px
;
padding-top
:
18px
;
}
.brand-mark
{
display
:
grid
;
width
:
58px
;
height
:
58px
;
place-items
:
center
;
border-radius
:
8px
;
background
:
var
(
--color-primary
);
color
:
white
;
font-size
:
20px
;
font-weight
:
900
;
}
.login-form
{
display
:
grid
;
gap
:
20px
;
padding
:
18px
;
}
.field
{
display
:
grid
;
gap
:
8px
;
color
:
var
(
--color-muted
);
font-size
:
14px
;
}
.field
input
{
width
:
100%
;
height
:
48px
;
border
:
1px
solid
var
(
--color-border
);
border-radius
:
8px
;
padding
:
0
14px
;
background
:
#fff
;
color
:
var
(
--color-text
);
outline
:
none
;
}
.field
input
:focus
{
border-color
:
var
(
--color-primary
);
}
</
style
>
frontend-h5/src/pages/game1/Game1.vue
View file @
8a5923a5
...
...
@@ -2,10 +2,11 @@
import
{
onBeforeUnmount
,
onMounted
,
ref
}
from
'vue'
import
{
cssAssetUrl
}
from
'@/commons/assets.ts'
import
MobileStage
from
'@/components/MobileStage.vue'
import
{
useGameSocket
}
from
'@/composables/useGameSocket'
import
{
useGameSocket
,
joinSharedRoom
,
sendGameMessage
,
userJoinStatus
}
from
'@/composables/useGameSocket'
import
LoadingView
from
'./views/LoadingView.vue'
import
PlayingView
from
'./views/PlayingView.vue'
import
ScoreView
from
'./views/ScoreView.vue'
import
{
$getWechat
}
from
'@/commons/utils.ts'
type
GameView
=
'loading'
|
'playing'
|
'score'
...
...
@@ -25,16 +26,20 @@ const imageUrls = {
replay
:
cssAssetUrl
(
'game1/replay.png'
),
}
const
wechat
=
$getWechat
()
const
currentView
=
ref
<
GameView
>
(
'loading'
)
const
countdownInterval
=
ref
(
60
)
const
isDivDescVisible
=
ref
(
false
)
const
tick
=
ref
(
0
)
const
rank
=
ref
(
8
)
const
level
=
ref
(
5
)
const
tick
=
ref
(
0
)
//点击次数
const
rank
=
ref
(
0
)
//排名
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
mobileStageRef
=
ref
<
InstanceType
<
typeof
MobileStage
>
|
null
>
(
null
)
const
_offSocketMessage
=
ref
<
(()
=>
void
)
|
undefined
>
()
let
guFrameTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
let
gameCountdownTimer
:
ReturnType
<
typeof
window
.
setTimeout
>
|
undefined
...
...
@@ -55,8 +60,22 @@ function stopGameCountdown() {
}
}
function
submitScore
()
{
function
submitScore
(
save
:
boolean
=
false
)
{
//最后提交保存在数据库中
if
(
save
)
{
//item_num 游戏项目(1到6)
//wechat 原始token
sendGameMessage
(
'submit_score_save'
,
{
score
:
tick
.
value
,
wechat
:
wechat
.
token_origin
,
item_num
:
1
,
nickname
:
wechat
.
nickname
,
avatar
:
wechat
.
avatar
,
rank
:
rank
.
value
,
})
}
else
{
sendGameMessage
(
'submit_score'
,
{
score
:
tick
.
value
})
}
}
function
startGameCountdown
(
seconds
=
60
)
{
...
...
@@ -71,7 +90,7 @@ function startGameCountdown(seconds = 60) {
countdownInterval
.
value
=
0
gameCountdownTimer
=
undefined
setDivDescVisible
(
false
)
submitScore
()
submitScore
(
true
)
showScoreView
()
return
}
...
...
@@ -93,46 +112,24 @@ function startGameView() {
tick
.
value
=
0
currentGu
.
value
=
guFrames
[
0
]
currentView
.
value
=
'playing'
startGameCountdown
(
60
)
}
async
function
startGameWithCountdown
()
{
await
mobileStageRef
.
value
?.
startCountdown
(
3
)
startGameView
()
startGameCountdown
()
}
function
showScoreView
()
{
stopGameCountdown
()
setDivDescVisible
(
false
)
level
.
value
=
rank
.
value
currentView
.
value
=
'score'
}
function
updateSubmittedRank
(
data
:
any
)
{
const
nextRank
=
Number
(
typeof
data
===
'object'
?
data
?.
rank
:
data
)
if
(
Number
.
isFinite
(
nextRank
)
&&
nextRank
>
0
)
{
rank
.
value
=
nextRank
level
.
value
=
nextRank
}
}
function
replayGame
()
{
startGameView
()
}
function
backToWaiting
()
{
showLoadingView
()
joinSharedRoom
()
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
)
}
const
{
userJoinStatus
,
joinSharedRoom
,
sendGameMessage
}
=
useGameSocket
({
gameId
:
'game1'
,
onGameStart
:
startGameWithCountdown
,
onScoreSubmitted
:
updateSubmittedRank
,
onRoomClosed
:
showLoadingView
,
})
const
touchHandler
=
()
=>
{
if
(
isGuPlaying
.
value
)
{
return
...
...
@@ -162,26 +159,65 @@ const touchHandler = () => {
playNextFrame
()
}
if
(
wechat
)
{
const
{
offSocketMessage
}
=
useGameSocket
({
gameId
:
'game1'
,
auth
:
{
userid
:
wechat
.
token
,
},
onConnect
:
()
=>
{
window
.
setTimeout
(()
=>
{
const
joined
=
joinSharedRoom
(
nickname
.
value
,
token
.
value
,
avatar
.
value
)
},
200
)
},
onGameStart
:
async
()
=>
{
await
mobileStageRef
.
value
?.
startCountdown
()
startGameView
()
},
onScoreSubmitted
:
(
is_save
,
data
)
=>
{
if
(
!
is_save
)
{
//非保存状态下
const
nextRank
=
Number
(
typeof
data
===
'object'
?
data
?.
rank
:
data
)
if
(
Number
.
isFinite
(
nextRank
)
&&
nextRank
>
0
)
{
rank
.
value
=
nextRank
}
}
},
onRescoreSubmitted
:
(
_recount
)
=>
{
submitScore
(
true
);
},
onRoomClosed
:
showLoadingView
,
})
_offSocketMessage
.
value
=
offSocketMessage
}
onMounted
(()
=>
{
if
(
token
.
value
)
{
window
.
addEventListener
(
'beforeunload'
,
confirmRefresh
)
}
})
onBeforeUnmount
(()
=>
{
_offSocketMessage
.
value
?.()
if
(
guFrameTimer
)
{
window
.
clearTimeout
(
guFrameTimer
)
}
stopGameCountdown
()
if
(
token
.
value
)
{
window
.
removeEventListener
(
'beforeunload'
,
confirmRefresh
)
}
})
</
script
>
<
template
>
<MobileStage
ref=
"mobileStageRef"
:background=
"`$
{imageUrls.bg} center/cover`">
<MobileStage
v-if=
"token"
ref=
"mobileStageRef"
:background=
"`$
{imageUrls.bg} center/cover`">
<LoadingView
v-if=
"currentView === 'loading'"
:image-urls=
"imageUrls"
:user-join-status=
"userJoinStatus"
/>
<PlayingView
v-else-if=
"currentView === 'playing'"
:image-urls=
"imageUrls"
:tick=
"tick"
:rank=
"rank"
:current-gu=
"currentGu"
:countdown-interval=
"countdownInterval"
:is-div-desc-visible=
"isDivDescVisible"
@
touch=
"touchHandler"
/>
<ScoreView
v-else
:image-urls=
"imageUrls"
:
tick=
"tick"
:level=
"level
"
@
replay=
"replayGame"
<ScoreView
v-else
:image-urls=
"imageUrls"
:
rank=
"rank"
:tick=
"tick
"
@
replay=
"replayGame"
@
back=
"backToWaiting"
/>
</MobileStage>
<div
v-else
style=
"text-align: center; width: 100vw; height: 100vh; line-height: 30; font-size: 20px;"
>
请使用微信扫码进入游戏
</div>
</
template
>
frontend-h5/src/pages/game1/views/LoadingView.vue
View file @
8a5923a5
<
script
setup
lang=
"ts"
>
import
{
$getWechat
}
from
'@/commons/utils.ts'
import
{
onMounted
,
ref
}
from
'vue'
;
defineProps
<
{
imageUrls
:
Record
<
string
,
string
>
// countdownInterval: number
// isDivDescVisible: boolean
userJoinStatus
:
boolean
}
>
()
}
>
();
const
nickname
=
ref
(
''
);
const
avatar
=
ref
(
''
);
onMounted
(()
=>
{
const
wechat
=
$getWechat
();
nickname
.
value
=
wechat
.
nickname
;
avatar
.
value
=
wechat
.
avatar
;
});
</
script
>
<
template
>
<div
class=
"h5-page game-stage"
>
<!--
<Transition
name=
"div-desc-slide"
appear
>
<div
v-if=
"isDivDescVisible"
class=
"div-desc"
>
<span
class=
"desc-clock"
aria-hidden=
"true"
></span>
<span
class=
"desc-time"
>
{{
countdownInterval
}}
</span>
</div>
</Transition>
-->
<div
class=
"game-desc"
>
游戏规则
</div>
<div
class=
"img-logo"
></div>
<div
class=
"img-title"
></div>
<div
class=
"img-avatar"
></div>
<div
class=
"txt-nickname"
>
张小明
</div>
<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"
>
<label
v-if=
"userJoinStatus"
>
您已成功加入游戏等待主持人开始
</label>
<label
v-else
>
正在加入游戏...
</label>
...
...
@@ -37,64 +39,6 @@ defineProps<{
padding
:
0
;
}
/* .div-desc {
position: absolute;
top: 150px;
left: var(--stage-viewport-left, 0);
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
width: 180px;
height: 80px;
border-radius: 0 56px 56px 0;
background: rgba(40, 12, 8, 0.48);
color: #fff;
.desc-clock {
position: absolute;
left: 20px;
width: 38px;
height: 44px;
border-radius: 50%;
background: v-bind('imageUrls.clock') center/cover;
}
.desc-time {
position: absolute;
right: 40px;
color: white;
font-size: 35pt;
line-height: 1;
}
} */
/*
.div-desc-slide-enter-active {
transition:
transform 360ms ease-out 220ms,
opacity 360ms ease-out 220ms;
}
.div-desc-slide-leave-active {
transition:
transform 260ms ease-in,
opacity 260ms ease-in;
}
.div-desc-slide-enter-from,
.div-desc-slide-leave-to {
opacity: 0;
transform: translateX(-110%);
}
.div-desc-slide-enter-to,
.div-desc-slide-leave-from {
opacity: 1;
transform: translateX(0);
} */
.game-desc
{
position
:
absolute
;
top
:
50%
;
...
...
@@ -150,15 +94,15 @@ defineProps<{
position
:
absolute
;
top
:
480px
;
left
:
50%
;
width
:
148px
;
/*
width: 148px;
height: 148px;
background
:
v-bind
(
'
imageUrls.avatar'
)
center
/
cover
;
background: v-bind('
avatar') center/cover; */
transform
:
translateX
(
-50%
);
}
.txt-nickname
{
position
:
absolute
;
top
:
6
5
0px
;
top
:
6
4
0px
;
left
:
50%
;
color
:
#E00000
;
font-size
:
25pt
;
...
...
frontend-h5/src/pages/game1/views/ScoreView.vue
View file @
8a5923a5
...
...
@@ -2,7 +2,7 @@
defineProps
<
{
imageUrls
:
Record
<
string
,
string
>
tick
:
number
level
:
number
rank
:
number
}
>
()
defineEmits
<
{
...
...
@@ -23,7 +23,7 @@ defineEmits<{
<div>
击鼓次数(次)
</div>
<div>
{{
tick
}}
</div>
<div>
最终排名
</div>
<div>
第
<label>
{{
level
}}
</label>
名
</div>
<div>
第
<label>
{{
rank
}}
</label>
名
</div>
</div>
</div>
<div
class=
"btn-group"
>
...
...
frontend-h5/src/router/index.ts
View file @
8a5923a5
import
{
createRouter
,
createWebHashHistory
}
from
'vue-router'
import
{
$read
}
from
'@/commons/utils'
import
Login
from
'@/pages/Login.vue'
import
Game1
from
'@/pages/game1/Game1.vue'
const
getWechatId
=
()
=>
{
return
$read
(
'
wechat_id
'
,
''
)
return
$read
(
'
token
'
,
''
)
}
const
router
=
createRouter
({
history
:
createWebHashHistory
(),
routes
:
[
{
path
:
'/'
,
redirect
:
()
=>
(
getWechatId
()
?
'/game1'
:
'/login'
),
},
{
path
:
'/login'
,
name
:
'Login'
,
component
:
Login
,
},
// {
// path: '/',
// redirect: () => (getWechatId() ? '/game1' : '/login'),
// },
{
path
:
'/game1'
,
name
:
'Game1'
,
component
:
Game1
,
meta
:
{
requiresAuth
:
true
,
},
//
meta: {
//
requiresAuth: true,
//
},
},
// {
// path: '/:pathMatch(.*)*',
...
...
frontend-h5/vite.config.ts
View file @
8a5923a5
...
...
@@ -6,7 +6,7 @@ import { defineConfig } from 'vite'
import
vue
from
'@vitejs/plugin-vue'
import
vueDevTools
from
'vite-plugin-vue-devtools'
const
buildDomain
=
'https://hddp
server
.guocai365.org.cn'
const
buildDomain
=
'https://hddp.guocai365.org.cn'
function
copyImageAssets
()
{
return
{
...
...
frontend-large/src/assets/images/game1.mp3
0 → 100644
View file @
8a5923a5
File added
frontend-large/src/assets/images/game1/qrcode.png
View replaced file @
948fdad3
View file @
8a5923a5
31.3 KB
|
W:
|
H:
695 Bytes
|
W:
|
H:
2-up
Swipe
Onion skin
frontend-large/src/commons/music.ts
0 → 100644
View file @
8a5923a5
import
game1MusicUrl
from
'@/assets/images/game1.mp3'
let
backgroundMusic
:
HTMLAudioElement
|
null
=
null
function
getBackgroundMusic
()
{
if
(
!
backgroundMusic
)
{
backgroundMusic
=
new
Audio
(
game1MusicUrl
)
backgroundMusic
.
loop
=
true
backgroundMusic
.
preload
=
'auto'
backgroundMusic
.
volume
=
0.6
backgroundMusic
.
load
()
}
return
backgroundMusic
}
export
async
function
playGame1Music
()
{
const
audio
=
getBackgroundMusic
()
try
{
await
audio
.
play
()
window
.
removeEventListener
(
'pointerdown'
,
playGame1Music
)
window
.
removeEventListener
(
'keydown'
,
playGame1Music
)
}
catch
{
window
.
addEventListener
(
'pointerdown'
,
playGame1Music
,
{
once
:
true
})
window
.
addEventListener
(
'keydown'
,
playGame1Music
,
{
once
:
true
})
}
}
export
function
stopGame1Music
()
{
window
.
removeEventListener
(
'pointerdown'
,
playGame1Music
)
window
.
removeEventListener
(
'keydown'
,
playGame1Music
)
backgroundMusic
?.
pause
()
}
frontend-large/src/commons/utils.ts
View file @
8a5923a5
...
...
@@ -74,7 +74,7 @@ export function $read_socket_storge(): any {
let
token
=
$read
(
'token'
,
''
);
const
socket_path
=
$read
(
'socket_path'
,
''
);
const
client_ns
=
$read
(
'client_ns'
,
''
)
const
domain
=
localStorage
.
getItem
(
'domain'
)
||
''
const
domain
=
$read
(
'domain'
,
''
);
return
{
passphrase
,
token
,
socket_path
,
client_ns
,
domain
}
...
...
@@ -88,8 +88,17 @@ export function $format_str(str: string, num: number) {
}
export
function
$is_run_local
():
boolean
{
if
(
/localhost|192.168/gim
.
test
(
location
.
href
))
{
if
(
/
https
?
:
\/\/
localhost|192.168/gim
.
test
(
location
.
href
))
{
return
true
;
}
return
false
;
}
export
function
$query
(
q
:
string
|
string
[]):
string
|
string
[]
{
const
query
=
location
.
href
.
match
(
/
\?([^
#
]
*
)
/
)?.[
1
]?.
replace
(
/
\/
$/
,
''
);
const
params
=
new
URLSearchParams
(
query
);
if
(
Array
.
isArray
(
q
))
{
return
q
.
map
((
key
)
=>
params
.
get
(
key
)).
filter
((
v
):
v
is
string
=>
!!
v
);
}
return
params
.
get
(
q
)
||
''
;
}
\ No newline at end of file
frontend-large/src/commons/ws.ts
View file @
8a5923a5
import
{
io
,
type
Socket
}
from
"socket.io-client"
;
import
{
decode
,
encode
}
from
"@msgpack/msgpack"
;
import
{
$is_run_local
}
from
"./utils"
;
export
type
SocketIOClientOptions
=
{
url
?:
string
;
...
...
@@ -36,7 +37,6 @@ export function initSocket(args: SocketInitArgs): Socket | null {
args
.
onConnect
?.(
socket
);
return
socket
;
}
socket
.
removeAllListeners
();
socket
.
disconnect
();
socket
=
null
;
...
...
@@ -56,7 +56,9 @@ export function initSocket(args: SocketInitArgs): Socket | null {
});
socket
.
onAny
((
evt
,
payload
)
=>
{
const
data
=
decode
(
payload
);
if
(
$is_run_local
())
{
console
.
log
(
evt
,
data
);
}
messageHandlers
.
forEach
((
handler
)
=>
handler
(
evt
,
data
));
});
...
...
frontend-large/src/composables/useAdminGameSocket.ts
View file @
8a5923a5
...
...
@@ -45,7 +45,7 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
const
{
passphrase
,
token
,
socket_path
,
client_ns
,
domain
}
=
$read_socket_storge
()
initSocket
({
socket_path
:
`
${
domain
}${
socket_path
}
`
,
client_ns
,
client_ns
:
"/ws"
,
auth
:
{
userid
:
token
,
validuser
:
CryptoJS
.
AES
.
encrypt
(
token
,
passphrase
).
toString
(),
...
...
@@ -58,9 +58,9 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
offSocketMessage
=
onSocketMessage
(
async
(
evt
,
payload
:
SocketPayload
)
=>
{
const
{
msg
,
state
,
data
}
=
payload
if
(
$goto_login
(
evt
,
router
,
options
.
loginRedirectPath
??
'/game1'
))
{
return
}
//
if ($goto_login(evt, router, options.loginRedirectPath ?? '/game1')) {
//
return
//
}
if
(
state
===
0
)
{
if
(
evt
===
'game_start_result'
&&
data
===
1
)
{
...
...
frontend-large/src/pages/Login.vue
View file @
8a5923a5
<
script
setup
lang=
"ts"
>
import
{
$save
}
from
'@/commons/utils.ts'
;
import
{
ref
}
from
'vue'
import
{
onMounted
,
ref
}
from
'vue'
import
{
useRouter
}
from
'vue-router'
import
DesignStage
from
'@/components/DesignStage.vue'
import
{
playGame1Music
}
from
'@/commons/music'
const
router
=
useRouter
()
const
username
=
ref
(
''
)
const
password
=
ref
(
''
)
...
...
@@ -16,8 +17,8 @@ async function login() {
},
body
:
JSON
.
stringify
({
username
:
username
.
value
,
password
:
password
.
value
}),
})
const
data
=
await
res
.
json
()
const
data
=
await
res
.
json
()
if
(
!
res
.
ok
||
data
.
state
!==
1
)
{
alert
(
data
.
msg
||
'Login failed'
)
throw
new
Error
(
data
.
msg
||
'Login failed'
)
...
...
@@ -27,6 +28,7 @@ async function login() {
}
const
loginHandler
=
async
()
=>
{
playGame1Music
()
const
{
state
,
msg
,
data
}
=
await
login
()
if
(
state
===
0
)
{
...
...
@@ -38,6 +40,10 @@ const loginHandler = async () => {
}
await
router
.
replace
(
'/game1'
)
}
onMounted
(()
=>
{
playGame1Music
()
})
</
script
>
<
template
>
...
...
frontend-large/src/pages/game1/Game1.vue
View file @
8a5923a5
...
...
@@ -5,6 +5,7 @@ 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
{
playGame1Music
}
from
'@/commons/music'
type
GameScreen
=
'loading'
|
'rank1'
|
'rank2'
...
...
@@ -108,6 +109,11 @@ const { startGame, createRoom, requestRoomState } = useAdminGameSocket({
onRoomRank
:
updateRankPlayers
,
})
const
startGameWithMusic
=
()
=>
{
playGame1Music
()
startGame
()
}
const
nextRound
=
()
=>
{
gotoLoading
()
createRoom
(
1
)
...
...
@@ -116,8 +122,14 @@ const nextRound = () => {
},
200
)
}
const
nextRoundWithMusic
=
()
=>
{
playGame1Music
()
nextRound
()
}
onMounted
(()
=>
{
window
.
addEventListener
(
'beforeunload'
,
confirmRefresh
)
playGame1Music
()
})
onUnmounted
(()
=>
{
...
...
@@ -126,7 +138,7 @@ onUnmounted(() => {
</
script
>
<
template
>
<Loading
v-if=
"screen === 'loading'"
:players=
"loadingPlayers"
:player-count=
"playerCount"
@
start=
"startGame"
/>
<Rank1
v-else-if=
"screen === 'rank1'"
ref=
"rank1Ref"
:rank-list=
"rankPlayers"
@
start=
"startGame"
/>
<Rank2
v-else
:rank-list=
"rankPlayers"
@
next=
"nextRound"
/>
<Loading
v-if=
"screen === 'loading'"
:players=
"loadingPlayers"
:player-count=
"playerCount"
@
start=
"startGame
WithMusic
"
/>
<Rank1
v-else-if=
"screen === 'rank1'"
ref=
"rank1Ref"
:rank-list=
"rankPlayers"
@
start=
"startGame
WithMusic
"
/>
<Rank2
v-else
:rank-list=
"rankPlayers"
@
next=
"nextRound
WithMusic
"
/>
</
template
>
frontend-large/src/pages/game1/views/LoadingView.vue
View file @
8a5923a5
...
...
@@ -57,7 +57,7 @@ const releaseStartButton = () => {
<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"
></div>
<div
class=
"img-avatar"
:style=
"`background:url('$
{item.avatar}') center / cover no-repeat;`"
>
</div>
</div>
<div
class=
"txt-nickname"
>
{{
$format_str
(
item
.
nickname
,
12
)
}}
</div>
</div>
...
...
@@ -205,7 +205,7 @@ const releaseStartButton = () => {
width
:
60px
;
height
:
60px
;
border-radius
:
30px
;
background
:
v-bind
(
'imageUrls.avatar'
);
/* background: v-bind('imageUrls.avatar'); */
}
.txt-nickname
{
...
...
frontend-large/src/pages/game1/views/Rank2View.vue
View file @
8a5923a5
...
...
@@ -54,6 +54,13 @@ watch(
(
rankList
)
=>
updateFinalRank
(
rankList
),
{
immediate
:
true
,
deep
:
true
},
);
const
renderAvatar
=
(
item
:
any
|
null
)
=>
{
if
(
item
&&
item
.
avatar
)
{
return
`background: url('
${
item
.
avatar
}
') center / cover no-repeat;`
}
return
`background: v-bind('imageUrls.avatar') center / cover no-repeat;`
}
</
script
>
<
template
>
...
...
@@ -66,21 +73,24 @@ watch(
<div
class=
"podium"
>
<div
class=
"rank-no1"
>
<div
class=
"gu-burst"
></div>
<div
class=
"avatar"
></div>
<div
class=
"avatar"
:style=
"`$
{renderAvatar(list[0])}`">
</div>
<div
class=
"nickname"
>
{{
list
[
0
]?.
nickname
}}
</div>
<div
class=
"guan"
></div>
<div
class=
"pai"
></div>
</div>
<div
class=
"rank-no2"
>
<div
class=
"gu-burst"
></div>
<div
class=
"avatar"
></div>
<div
class=
"avatar"
:style=
"`$
{renderAvatar(list[1])}`">
</div>
<div
class=
"nickname"
>
{{
list
[
1
]?.
nickname
}}
</div>
<div
class=
"guan"
></div>
<div
class=
"pai"
></div>
</div>
<div
class=
"rank-no3"
>
<div
class=
"gu-burst"
></div>
<div
class=
"avatar"
></div>
<div
class=
"avatar"
:style=
"`$
{renderAvatar(list[2])}`">
</div>
<div
class=
"nickname"
>
{{
list
[
2
]?.
nickname
}}
</div>
<div
class=
"guan"
></div>
<div
class=
"pai"
></div>
...
...
@@ -172,7 +182,7 @@ watch(
}
.avatar
{
background
:
v-bind
(
'imageUrls.avatar'
)
center
/
cover
no-repeat
;
/* background: v-bind('imageUrls.avatar') center / cover no-repeat; */
width
:
120px
;
height
:
120px
;
border-radius
:
60px
;
...
...
frontend-large/vite.config.ts
View file @
8a5923a5
...
...
@@ -6,7 +6,7 @@ import { defineConfig } from 'vite'
import
vue
from
'@vitejs/plugin-vue'
import
vueDevTools
from
'vite-plugin-vue-devtools'
const
buildDomain
=
'https://hddp
server
.guocai365.org.cn'
const
buildDomain
=
'https://hddp.guocai365.org.cn'
function
copyImageAssets
()
{
return
{
...
...
@@ -28,7 +28,8 @@ function copyImageAssets() {
export
default
defineConfig
(({
command
})
=>
({
base
:
command
===
'build'
?
'/cc/pc/'
:
'/'
,
define
:
{
__APP_DOMAIN__
:
JSON
.
stringify
(
command
===
'build'
?
buildDomain
:
''
),
// __APP_DOMAIN__: JSON.stringify(command === 'build' ? buildDomain : ''),
__APP_DOMAIN__
:
JSON
.
stringify
(
buildDomain
),
},
plugins
:
[
vue
(),
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment