mirror of
https://ghproxy.com/https://github.com/StreakingMan/solvable-sheep-game
synced 2026-10-02 23:44:59 +08:00
283 lines
10 KiB
TypeScript
283 lines
10 KiB
TypeScript
import React, { FC, Suspense, useEffect, useRef, useState } from 'react';
|
|
import style from './Score.module.scss';
|
|
import Bmob from 'hydrogen-js-sdk';
|
|
import {
|
|
PLAYING_THEME_ID_STORAGE_KEY,
|
|
randomString,
|
|
timestampToUsedTimeString,
|
|
USER_ID_STORAGE_KEY,
|
|
USER_NAME_STORAGE_KEY,
|
|
} from '../utils';
|
|
import WxQrCode from './WxQrCode';
|
|
|
|
const Fireworks = React.lazy(() => import('./Fireworks'));
|
|
|
|
interface RankInfo {
|
|
// id
|
|
objectId?: string;
|
|
// 综合评分
|
|
rating: number;
|
|
// 通关数
|
|
level: number;
|
|
// 游戏得分
|
|
score: number;
|
|
// 主题id
|
|
themeId: string;
|
|
// 耗时
|
|
time: number;
|
|
// 用户昵称
|
|
username: string;
|
|
// 用户id
|
|
userId: string;
|
|
}
|
|
|
|
// 该组件条件渲染
|
|
const Score: FC<{
|
|
level: number;
|
|
score: number;
|
|
time: number;
|
|
success: boolean;
|
|
restartMethod: () => void;
|
|
}> = ({ level, score, time, success, restartMethod }) => {
|
|
const [rankList, setRankList] = useState<RankInfo[]>([]);
|
|
const [username, setUsername] = useState<string>(
|
|
localStorage.getItem(USER_NAME_STORAGE_KEY) || ''
|
|
);
|
|
const [userId, setUserId] = useState<string>(
|
|
localStorage.getItem(USER_ID_STORAGE_KEY) || ''
|
|
);
|
|
const usernameInputRef = useRef<HTMLInputElement>(null);
|
|
const [tip, setTip] = useState<string>('');
|
|
|
|
// 综合评分
|
|
const rating = Math.max(0, score) * 100 - Math.round(time / 1000);
|
|
// 分主题排行
|
|
const themeId = localStorage.getItem(PLAYING_THEME_ID_STORAGE_KEY);
|
|
|
|
const uploadRankInfo = (id?: string) => {
|
|
const _userId = localStorage.getItem(USER_ID_STORAGE_KEY);
|
|
const _username = localStorage.getItem(USER_NAME_STORAGE_KEY);
|
|
if (!themeId || !_userId || !_username) return;
|
|
const rankInfo: RankInfo = {
|
|
rating,
|
|
themeId,
|
|
level,
|
|
score,
|
|
time,
|
|
username: _username,
|
|
userId: _userId,
|
|
};
|
|
const query = Bmob.Query('rank');
|
|
id && query.set('id', id);
|
|
for (const [key, val] of Object.entries(rankInfo)) {
|
|
query.set(key, val);
|
|
}
|
|
query
|
|
.save()
|
|
.then(() => {
|
|
getRankList();
|
|
})
|
|
.catch((e) => {
|
|
console.log(e);
|
|
});
|
|
};
|
|
|
|
const getRankList = (cb?: (rankList: RankInfo[]) => void) => {
|
|
if (!themeId) return;
|
|
const query = Bmob.Query('rank');
|
|
query.equalTo('themeId', '==', themeId);
|
|
query.order('-rating');
|
|
query.limit(50);
|
|
query
|
|
.find()
|
|
.then((res) => {
|
|
setRankList(res as any);
|
|
cb && cb(res as any);
|
|
const _userId = localStorage.getItem(USER_ID_STORAGE_KEY);
|
|
if (_userId) {
|
|
setTimeout(() => {
|
|
const rankEl = document.getElementById(_userId + 'el');
|
|
rankEl?.scrollIntoView({ behavior: 'smooth' });
|
|
});
|
|
}
|
|
})
|
|
.catch((e) => {
|
|
console.log(e);
|
|
});
|
|
};
|
|
|
|
const onConfirmNameClick = () => {
|
|
const inputUsername = usernameInputRef.current?.value;
|
|
if (!inputUsername) return;
|
|
const newUserId = randomString(8);
|
|
setUsername(inputUsername);
|
|
setUserId(newUserId);
|
|
localStorage.setItem(USER_NAME_STORAGE_KEY, inputUsername);
|
|
localStorage.setItem(USER_ID_STORAGE_KEY, newUserId);
|
|
judgeAndUpload(rankList, newUserId);
|
|
};
|
|
|
|
// 判断是否需要上传记录
|
|
const judgeAndUpload = (_rankList: RankInfo[], _userId: string) => {
|
|
if (!_userId) return;
|
|
if (
|
|
_rankList.length < 50 ||
|
|
rating > _rankList[_rankList.length - 1].rating
|
|
) {
|
|
// 榜未满或者分数高于榜上最后一名
|
|
// 本次排名
|
|
let thisRank = _rankList.findIndex((rank) => rank.rating < rating);
|
|
if (thisRank === -1) {
|
|
thisRank = _rankList.length + 1;
|
|
} else {
|
|
thisRank++;
|
|
}
|
|
// 查找是否曾上榜
|
|
const findSelf = _rankList.findIndex(
|
|
(rank) => rank.userId === _userId
|
|
);
|
|
if (findSelf === -1) {
|
|
// 新上榜
|
|
uploadRankInfo();
|
|
setTip(`恭喜上榜!本次排名${thisRank}`);
|
|
} else {
|
|
if (_rankList[findSelf].rating < rating) {
|
|
// 破自己记录
|
|
uploadRankInfo(_rankList[findSelf].objectId);
|
|
setTip(`个人新高!本次排名${thisRank}`);
|
|
} else if (_rankList[findSelf].rating > rating) {
|
|
// 没破自己记录
|
|
setTip(
|
|
`距离你的最高记录${_rankList[findSelf].rating}还差一点~`
|
|
);
|
|
} else {
|
|
setTip(`与你的最高记录${_rankList[findSelf].rating}持平~`);
|
|
}
|
|
}
|
|
} else {
|
|
// 未上榜
|
|
setTip('本次未上榜');
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (!__DIY__) {
|
|
// 排行榜
|
|
getRankList((rankList) =>
|
|
judgeAndUpload(
|
|
rankList,
|
|
localStorage.getItem(USER_ID_STORAGE_KEY) || ''
|
|
)
|
|
);
|
|
}
|
|
}, []);
|
|
|
|
return (
|
|
<div className={style.modal}>
|
|
<Suspense fallback={<span>fireworks</span>}>
|
|
{success && <Fireworks />}
|
|
</Suspense>
|
|
<div className={style.inner}>
|
|
{success ? <h1>🎉恭喜通关!</h1> : <h1>😫就差一点!</h1>}
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>通关数</th>
|
|
<th>用时</th>
|
|
<th>得分</th>
|
|
<th>综合评分</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>{level}</td>
|
|
<td>{timestampToUsedTimeString(time)}</td>
|
|
<td>{score}</td>
|
|
<td>{rating}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
{!__DIY__ && !username && (
|
|
<div className={'flex-container flex-center'}>
|
|
<input
|
|
className={style.nameInput}
|
|
ref={usernameInputRef}
|
|
placeholder={'留下大名进行排行榜pk!'}
|
|
/>
|
|
<button
|
|
className={'primary'}
|
|
onClick={onConfirmNameClick}
|
|
>
|
|
确定
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
<div>{tip}</div>
|
|
|
|
{!__DIY__ && (
|
|
<div className={style.rankContainer}>
|
|
<h1 className={style.title}>TOP 50</h1>
|
|
{rankList.length ? (
|
|
<div className={style.list}>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>名次</th>
|
|
<th>名称</th>
|
|
<th>通关数</th>
|
|
{/*<th>用时</th>*/}
|
|
{/*<th>得分</th>*/}
|
|
<th>综合评分</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{rankList.map((rank, idx) => (
|
|
<tr
|
|
key={idx}
|
|
id={rank.userId}
|
|
style={{
|
|
background:
|
|
rank.userId === userId
|
|
? 'rgb(0 0 0 / 20%)'
|
|
: '',
|
|
}}
|
|
>
|
|
<td>{idx + 1}</td>
|
|
<td>
|
|
{rank.username}
|
|
{rank.userId === userId &&
|
|
'(你)'}
|
|
</td>
|
|
<td>{rank.level}</td>
|
|
{/*<td>*/}
|
|
{/* {timestampToUsedTimeString(*/}
|
|
{/* rank.time*/}
|
|
{/* )}*/}
|
|
{/*</td>*/}
|
|
{/*<td>{rank.score}</td>*/}
|
|
<td>{rank.rating}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
) : (
|
|
<div className={style.tip}>
|
|
暂无排行,速速霸榜!
|
|
</div>
|
|
)}
|
|
<button className={'primary'} onClick={restartMethod}>
|
|
再来一次
|
|
</button>
|
|
</div>
|
|
)}
|
|
<WxQrCode />
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Score;
|