import type { AthleteStanding, Event, EventResultGroup, EventResultRow, SnapshotMessage, StandingRow, Team, } from '@/shared' import type { DisplaySettings } from './guards' import { DEFAULT_ROTATE_SECONDS } from './guards' /** * 后端未就绪时的降级数据(任务要求:降级 mock 并标注「示例数据」)。 * * 数值是手写的固定值而不是随机数:现场调试时截图对比不会因为每次刷新而变。 */ const TEAM_NAMES: readonly string[] = [ '第一分公司', '机关本部', '第二分公司', '第三分公司', '第四分公司', '第五分公司', '第六分公司', ] function team(index: number, code: string): Team { const name = TEAM_NAMES[index] ?? `单位${index + 1}` return { id: index + 1, code, name, shortName: null, logoPath: null, color: null, sortOrder: index, active: true, } } const MOCK_TEAMS: Team[] = [ team(0, 'T01'), team(1, 'T02'), team(2, 'T03'), team(3, 'T04'), team(4, 'T05'), team(5, 'T06'), team(6, 'T07'), ] interface StandingSeed { rank: number totalPoints: number gold: number silver: number bronze: number rankChange: number | null } const STANDING_SEEDS: readonly StandingSeed[] = [ { rank: 1, totalPoints: 186, gold: 8, silver: 5, bronze: 3, rankChange: 1 }, { rank: 2, totalPoints: 172, gold: 6, silver: 7, bronze: 4, rankChange: -1 }, { rank: 3, totalPoints: 158, gold: 5, silver: 4, bronze: 8, rankChange: 0 }, { rank: 4, totalPoints: 141, gold: 4, silver: 5, bronze: 6, rankChange: 2 }, { rank: 5, totalPoints: 127, gold: 3, silver: 4, bronze: 5, rankChange: null }, { rank: 6, totalPoints: 103, gold: 2, silver: 3, bronze: 4, rankChange: 0 }, { rank: 7, totalPoints: 95, gold: 2, silver: 2, bronze: 2, rankChange: -2 }, ] function buildStandings(): StandingRow[] { return MOCK_TEAMS.map((item, index) => { const seed = STANDING_SEEDS[index] ?? STANDING_SEEDS[STANDING_SEEDS.length - 1] const row = seed ?? { rank: index + 1, totalPoints: 0, gold: 0, silver: 0, bronze: 0, rankChange: null } return { teamId: item.id, teamCode: item.code, teamName: item.name, shortName: item.shortName, color: item.color, logoPath: item.logoPath, rank: row.rank, gold: row.gold, silver: row.silver, bronze: row.bronze, medalTotal: row.gold + row.silver + row.bronze, totalPoints: row.totalPoints, rankChange: row.rankChange, } }) } function buildEvents(): Event[] { const seeds: readonly [string, string, Event['resultType'], Event['compare']][] = [ ['E1001', '男子 100 米', 'time', 'asc'], ['E1002', '女子 100 米', 'time', 'asc'], ['E2001', '男子跳远', 'distance', 'desc'], ['E3001', '女子 4×100 米接力', 'time', 'asc'], ['E4001', '拔河', 'count', 'desc'], ] return seeds.map(([code, name, resultType, compare], index) => ({ id: index + 1, sportId: 1, code, name, eventType: index === 3 || index === 4 ? 'team' : 'individual', resultMode: 'auto', resultType, compare, recordEligible: index < 3, scored: true, // 示例大屏里每个小项都正常结算奖牌(缺字段时 guards 也按 true 处理)。 awardMedals: true, multiplier: 1, status: index < 3 ? 'published' : 'finished', scheduledAt: null, venue: null, sortOrder: index, remark: null, avgMode: 'valid', })) } interface ResultSeed { rank: number teamIndex: number athlete: string resultText: string resultValue: number } function buildRows(seeds: readonly ResultSeed[], unit: string): EventResultRow[] { return seeds.map((seed, index) => { const team = MOCK_TEAMS[seed.teamIndex] return { entryId: (seed.rank + 1) * 100 + index, rank: seed.rank, teamId: team?.id ?? 0, teamCode: team?.code ?? '', teamName: team?.name ?? '', shortName: team?.shortName ?? null, logoPath: team?.logoPath ?? null, athleteId: index + 1, athleteName: seed.athlete, memberNames: null, resultText: seed.resultText, resultValue: seed.resultValue, unit, isTie: false, status: 'ok', } }) } function buildLatestResults(): EventResultGroup[] { return [ { eventId: 1, eventCode: 'E1001', eventName: '男子 100 米', categoryId: 1, categoryName: '青年组', publishedAt: '2026-09-09T15:42:00+08:00', rows: buildRows( [ { rank: 1, teamIndex: 0, athlete: '张伟', resultText: '11.58', resultValue: 11.58 }, { rank: 2, teamIndex: 2, athlete: '李强', resultText: '11.72', resultValue: 11.72 }, { rank: 3, teamIndex: 1, athlete: '王磊', resultText: '11.85', resultValue: 11.85 }, { rank: 4, teamIndex: 4, athlete: '刘洋', resultText: '11.94', resultValue: 11.94 }, { rank: 5, teamIndex: 3, athlete: '陈晨', resultText: '12.03', resultValue: 12.03 }, { rank: 6, teamIndex: 5, athlete: '赵鹏', resultText: '12.15', resultValue: 12.15 }, ], 's', ), }, { eventId: 2, eventCode: 'E1002', eventName: '女子 100 米', categoryId: 2, categoryName: '青年组', publishedAt: '2026-09-09T15:10:00+08:00', rows: buildRows( [ { rank: 1, teamIndex: 1, athlete: '李娜', resultText: '13.04', resultValue: 13.04 }, { rank: 2, teamIndex: 0, athlete: '孙悦', resultText: '13.21', resultValue: 13.21 }, { rank: 3, teamIndex: 3, athlete: '周敏', resultText: '13.36', resultValue: 13.36 }, { rank: 4, teamIndex: 6, athlete: '吴倩', resultText: '13.48', resultValue: 13.48 }, { rank: 5, teamIndex: 2, athlete: '郑爽', resultText: '13.55', resultValue: 13.55 }, { rank: 6, teamIndex: 6, athlete: '冯雪', resultText: '13.62', resultValue: 13.62 }, ], 's', ), }, { eventId: 3, eventCode: 'E2001', eventName: '男子跳远', categoryId: 3, categoryName: '中年组', publishedAt: '2026-09-09T14:35:00+08:00', rows: buildRows( [ { rank: 1, teamIndex: 2, athlete: '黄涛', resultText: '6.42', resultValue: 6.42 }, { rank: 2, teamIndex: 5, athlete: '徐斌', resultText: '6.18', resultValue: 6.18 }, { rank: 3, teamIndex: 0, athlete: '马超', resultText: '6.05', resultValue: 6.05 }, { rank: 4, teamIndex: 3, athlete: '朱峰', resultText: '5.96', resultValue: 5.96 }, { rank: 5, teamIndex: 1, athlete: '胡军', resultText: '5.88', resultValue: 5.88 }, { rank: 6, teamIndex: 4, athlete: '林凯', resultText: '5.71', resultValue: 5.71 }, ], 'm', ), }, ] } const ATHLETE_SEEDS: readonly [string, number, number, number, number, number][] = [ ['张伟', 0, 42, 2, 1, 0], ['李娜', 1, 38, 2, 0, 1], ['李强', 2, 34, 1, 2, 0], ['孙悦', 0, 30, 1, 1, 1], ['黄涛', 2, 28, 1, 0, 2], ['王磊', 1, 26, 1, 1, 0], ['周敏', 3, 24, 1, 0, 1], ['刘洋', 4, 22, 0, 2, 1], ['徐斌', 5, 20, 1, 0, 1], ['陈晨', 3, 18, 0, 1, 2], ['马超', 0, 16, 0, 1, 1], ['吴倩', 6, 14, 0, 0, 2], ] function buildAthletes(): AthleteStanding[] { return ATHLETE_SEEDS.map(([name, teamIndex, totalPoints, gold, silver, bronze], index) => { const item = MOCK_TEAMS[teamIndex] return { rank: index + 1, athleteId: index + 1, athleteName: name, gender: null, teamId: item?.id ?? 0, teamCode: item?.code ?? '', teamName: item?.name ?? '', shortName: item?.shortName ?? null, logoPath: item?.logoPath ?? null, totalPoints, gold, silver, bronze, } }) } /** * 示例配置。单列出来是因为它比共享契约多「实时赛况」两个键(后端未就绪时也要能演示): * 直接写在 SnapshotMessage 字面量里会被多余属性检查拦下。 */ const MOCK_SETTINGS: DisplaySettings = { event: { name: '2026 职工运动会', subtitle: null, host: '示例组委会', logoPath: null, primaryColor: '#4fd8ff', }, display: { background: null, logo: null, title: '2026 职工运动会', subtitle: '积分榜 · 示例数据', primaryColor: '#4fd8ff', // 与服务端默认一致(dto/settings.go 的 display.rotate_seconds 兜底 15)。 rotateSeconds: DEFAULT_ROTATE_SECONDS, // 示例数据里有 3 个已完成项目,取前两个演示双栏分栏。 liveEvents: [1, 2], liveSource: 'manual', }, standings: { individualToTeamWeight: 1, rankingMode: 'points', showIndividualBoard: true, showIndividualMedals: true, }, defaultProfileId: null, requireApproval: false, } export function createMockSnapshot(): SnapshotMessage { return { // version 固定 0 且不写入缓存:真实快照一到就能直接覆盖,不会因为版本号更大而被丢弃。 version: 0, settings: MOCK_SETTINGS, teams: MOCK_TEAMS, events: buildEvents(), standings: buildStandings(), records: [], latestResults: buildLatestResults(), } } export function createMockAthletes(): AthleteStanding[] { return buildAthletes() }