feat(admin,player,api): 玩家账号密码管理与代理上下分
新增玩家头像、可查密码与全局改密/改账号开关;玩家资料页合并账号密码展示;代理直属玩家列表支持自定义上下分。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
104
packages/shared/src/builtinPlayers.ts
Normal file
104
packages/shared/src/builtinPlayers.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
export type BuiltinPlayer = {
|
||||
id: string;
|
||||
name: string;
|
||||
position: string;
|
||||
country: string;
|
||||
filename: string;
|
||||
};
|
||||
|
||||
const BUILTIN_PLAYER_FILENAMES = [
|
||||
'何塞·曼努埃尔·洛佩斯-前锋-阿根廷.jpg',
|
||||
'佩德里-中场-西班牙.jpg',
|
||||
'卢卡·莫德里奇-中场-克罗地亚.jpg',
|
||||
'华金·皮克雷斯-中场-乌拉圭.jpg',
|
||||
'乔治亚·德·阿拉斯凯塔-中场-乌拉圭.jpg',
|
||||
'乌古尔坎·卡基尔-守門員-土耳其.jpg',
|
||||
'亚历杭德罗·曾德哈斯-前锋-美国.jpg',
|
||||
'恩德里克-前锋-巴西.jpg',
|
||||
'克里斯蒂亚诺·罗纳尔多-前锋-葡萄牙.jpg',
|
||||
'克里斯蒂安·罗梅罗-后卫-阿根廷.jpg',
|
||||
'内马尔-前锋-巴西.jpg',
|
||||
'凯南·耶尔德兹-前锋-土耳其.jpg',
|
||||
'卡塞米罗-中场-巴西.jpg',
|
||||
'卢卡斯·帕奎塔-中场-巴西.jpg',
|
||||
'基利安·姆巴佩-前锋-法国.jpg',
|
||||
'孟菲斯·德派-前锋-荷兰.jpg',
|
||||
'奥斯曼·登贝莱-前锋-法国.jpg',
|
||||
'布鲁诺·吉马良斯-中场-巴西.jpg',
|
||||
'布鲁诺·费尔南德斯-中场-葡萄牙.jpg',
|
||||
'布卡约·萨卡-前锋-英格兰.jpg',
|
||||
'德尼兹·居尔-前锋-土耳其.jpg',
|
||||
'德尼兹·温达夫-前锋-德国.jpg',
|
||||
'拉斐尔·迪亚斯·贝洛利-前锋-巴西.jpg',
|
||||
'拉明·亚马尔-前锋-西班牙.jpg',
|
||||
'朱利安·阿尔瓦雷斯-前锋-阿根廷.jpg',
|
||||
'梅西-前锋-阿根廷.jpg',
|
||||
'迈克尔·奥利塞-前锋-法国.jpg',
|
||||
'穆罕默德·萨拉赫-前锋-埃及.jpg',
|
||||
'维尼修斯·儒尼奥尔-前锋-巴西.jpg',
|
||||
'维克托·哲凯赖什-前锋-瑞典.jpg',
|
||||
'圣地亚哥·吉梅内斯-前锋-墨西哥.jpg',
|
||||
'埃德森·阿尔瓦雷斯-后卫-墨西哥.jpg',
|
||||
'埃贝雷奇·埃泽-中场-英格兰.jpg',
|
||||
'埃尔林·哈兰德-前锋-挪威.jpg',
|
||||
'蒂博·库尔图瓦-守門員-比利时.jpg',
|
||||
'曼努埃尔·诺伊尔-守門員-德国.jpg',
|
||||
'祖德·贝林厄姆-中场-英格兰.jpg',
|
||||
'伦纳特·卡尔-中场-德国.jpg',
|
||||
'费德里科·巴尔韦德-中场-乌拉圭.jpg',
|
||||
'贾马尔·慕斯拉-中场-德国.jpg',
|
||||
'路易斯·迪亚斯-前锋-哥伦比亚.jpg',
|
||||
'阿什拉夫·哈基米-后卫-摩洛哥.jpg',
|
||||
'阿利松·贝克尔-守門員-巴西.jpg',
|
||||
'阿尔达·居莱尔-前锋-土耳其.jpg',
|
||||
'马西斯·拉扬·切尔基-中场-法国.jpg',
|
||||
'马库斯·拉什福德-前锋-英格兰.jpg',
|
||||
'哈里·凯恩-前锋-英格兰.jpg',
|
||||
'尼科·威廉斯-前锋-西班牙.jpg',
|
||||
'巴勃罗·加维-中场-西班牙.jpg',
|
||||
'吉列尔莫·奥乔亚-守門員-墨西哥.jpg',
|
||||
] as const;
|
||||
|
||||
function parsePlayerFilename(filename: string): BuiltinPlayer {
|
||||
const base = filename.replace(/\.jpg$/i, '');
|
||||
const parts = base.split('-');
|
||||
const country = parts.pop() ?? '';
|
||||
const position = parts.pop() ?? '';
|
||||
const name = parts.join('-');
|
||||
return { id: base, name, position, country, filename };
|
||||
}
|
||||
|
||||
export const BUILTIN_PLAYERS: BuiltinPlayer[] = BUILTIN_PLAYER_FILENAMES.map(parsePlayerFilename);
|
||||
|
||||
const AVATAR_KEY_SET = new Set(BUILTIN_PLAYERS.map((p) => p.id));
|
||||
|
||||
export function isValidAvatarKey(key: string | null | undefined): boolean {
|
||||
if (!key) return true;
|
||||
return AVATAR_KEY_SET.has(key);
|
||||
}
|
||||
|
||||
export function playerAvatarUrl(key: string | null | undefined): string | null {
|
||||
if (!key) return null;
|
||||
const player = BUILTIN_PLAYERS.find((p) => p.id === key);
|
||||
if (!player) return null;
|
||||
return `/球员/${player.filename}`;
|
||||
}
|
||||
|
||||
export function getBuiltinPlayer(key: string | null | undefined): BuiltinPlayer | null {
|
||||
if (!key) return null;
|
||||
return BUILTIN_PLAYERS.find((p) => p.id === key) ?? null;
|
||||
}
|
||||
|
||||
/** 按 seed 稳定随机,无 seed 时完全随机 */
|
||||
export function randomAvatarKey(seed?: string | number | null): string {
|
||||
if (!BUILTIN_PLAYERS.length) return '';
|
||||
if (seed === undefined || seed === null || seed === '') {
|
||||
return BUILTIN_PLAYERS[Math.floor(Math.random() * BUILTIN_PLAYERS.length)].id;
|
||||
}
|
||||
const text = String(seed);
|
||||
let hash = 0;
|
||||
for (let i = 0; i < text.length; i += 1) {
|
||||
hash = (hash * 31 + text.charCodeAt(i)) >>> 0;
|
||||
}
|
||||
return BUILTIN_PLAYERS[hash % BUILTIN_PLAYERS.length].id;
|
||||
}
|
||||
Reference in New Issue
Block a user