diff --git a/lib/app/config/api_common.dart b/lib/app/config/api_common.dart index 54871bc..89e92c4 100644 --- a/lib/app/config/api_common.dart +++ b/lib/app/config/api_common.dart @@ -12,7 +12,7 @@ enum AuthApi { getTeamList('/api/events/device/item/group'), /// 参赛队伍详情 - getTeamDetail('/api/events/device/schedule/pending/score'), + getTeamDetail('/api/events/device/schedule/pending'), /// 获取选手的赛事信息 playerRegistrationList('/api/events/device/player/registration/list'); diff --git a/lib/features/auth/pages/page_auth.dart b/lib/features/auth/pages/page_auth.dart index 93aa8e0..5aa648e 100644 --- a/lib/features/auth/pages/page_auth.dart +++ b/lib/features/auth/pages/page_auth.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:recording_tool/app/router/app_navigator.dart'; @@ -23,7 +24,7 @@ class _AuthPageWidgetState extends ConsumerState { void initState() { super.initState(); _controller = TextEditingController(); - // _controller?.text = '299689'; + _controller?.text = '986570'; WidgetsBinding.instance.addPostFrameCallback((_) async { final token = AppStorage.getString(StorageKeys.authToken); @@ -59,7 +60,15 @@ class _AuthPageWidgetState extends ConsumerState { padding: EdgeInsets.symmetric(horizontal: 20.w), child: Column( children: [ - AppTextField(controller: _controller), + AppTextField( + controller: _controller, + keyboardType: TextInputType.number, + maxLength: 6, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(6), + ], + ), SizedBox(height: 20.h), SizedBox( width: double.maxFinite, diff --git a/lib/features/competition_teams/model/model_competition_team.dart b/lib/features/competition_teams/model/model_competition_team.dart index 499961f..b307257 100644 --- a/lib/features/competition_teams/model/model_competition_team.dart +++ b/lib/features/competition_teams/model/model_competition_team.dart @@ -169,3 +169,161 @@ class CompetitionTeamPageResult { ); } } + +/// 参赛队伍详情 +class CompetitionTeamDetail { + String? scheduleId; + String? itemId; + String? eventId; + String? itemName; + String? groupName; + String? matchPlace; + String? matchStartTime; + String? matchEndTime; + List? matchups; + + CompetitionTeamDetail({ + this.scheduleId, + this.itemId, + this.eventId, + this.itemName, + this.groupName, + this.matchPlace, + this.matchStartTime, + this.matchEndTime, + this.matchups, + }); + + factory CompetitionTeamDetail.fromJson(Map json) => + CompetitionTeamDetail( + scheduleId: json['scheduleId']?.toString(), + itemId: json['itemId']?.toString(), + eventId: json['eventId']?.toString(), + itemName: json['itemName']?.toString(), + groupName: json['groupName']?.toString(), + matchPlace: json['matchPlace']?.toString(), + matchStartTime: json['matchStartTime']?.toString(), + matchEndTime: json['matchEndTime']?.toString(), + matchups: json['matchups'] == null + ? const [] + : List.from( + (json['matchups'] as List).whereType().map( + (x) => Matchup.fromJson(Map.from(x)), + ), + ), + ); + + /// 兼容 data 为对象或数组(取首项) + factory CompetitionTeamDetail.fromResponse(dynamic json) { + if (json is List) { + if (json.isEmpty) return CompetitionTeamDetail(matchups: const []); + final first = json.first; + if (first is Map) { + return CompetitionTeamDetail.fromJson(Map.from(first)); + } + return CompetitionTeamDetail(matchups: const []); + } + if (json is Map) { + return CompetitionTeamDetail.fromJson(Map.from(json)); + } + return CompetitionTeamDetail(matchups: const []); + } + + Map toJson() => { + 'scheduleId': scheduleId, + 'itemId': itemId, + 'eventId': eventId, + 'itemName': itemName, + 'groupName': groupName, + 'matchPlace': matchPlace, + 'matchStartTime': matchStartTime, + 'matchEndTime': matchEndTime, + 'matchups': matchups == null + ? [] + : List.from(matchups!.map((x) => x.toJson())), + }; +} + +class Matchup { + String? matchTitle; + List? teamA; + List? teamB; + + Matchup({this.matchTitle, this.teamA, this.teamB}); + + factory Matchup.fromJson(Map json) => Matchup( + matchTitle: json['matchTitle']?.toString(), + teamA: json['teamA'] == null + ? const [] + : List.from( + (json['teamA'] as List).whereType().map( + (x) => Team.fromJson(Map.from(x)), + ), + ), + teamB: json['teamB'] == null + ? const [] + : List.from( + (json['teamB'] as List).whereType().map( + (x) => Team.fromJson(Map.from(x)), + ), + ), + ); + + Map toJson() => { + 'matchTitle': matchTitle, + 'teamA': teamA == null + ? [] + : List.from(teamA!.map((x) => x.toJson())), + 'teamB': teamB == null + ? [] + : List.from(teamB!.map((x) => x.toJson())), + }; +} + +class Team { + String? id; + String? teamName; + List? players; + + Team({this.id, this.teamName, this.players}); + + factory Team.fromJson(Map json) => Team( + id: json['id']?.toString(), + teamName: json['teamName']?.toString(), + players: json['players'] == null + ? const [] + : List.from( + (json['players'] as List).whereType().map( + (x) => Player.fromJson(Map.from(x)), + ), + ), + ); + + Map toJson() => { + 'id': id, + 'teamName': teamName, + 'players': players == null + ? [] + : List.from(players!.map((x) => x.toJson())), + }; +} + +class Player { + String? id; + String? name; + bool? isLeader; + + Player({this.id, this.name, this.isLeader}); + + factory Player.fromJson(Map json) => Player( + id: json['id']?.toString(), + name: json['name']?.toString(), + isLeader: json['isLeader'] as bool?, + ); + + Map toJson() => { + 'id': id, + 'name': name, + 'isLeader': isLeader, + }; +} diff --git a/lib/features/competition_teams/pages/page_competition_team_detail.dart b/lib/features/competition_teams/pages/page_competition_team_detail.dart index 89a2a90..ea82861 100644 --- a/lib/features/competition_teams/pages/page_competition_team_detail.dart +++ b/lib/features/competition_teams/pages/page_competition_team_detail.dart @@ -2,56 +2,91 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:recording_tool/features/competition_teams/model/model_competition_team.dart'; -import 'package:recording_tool/features/competition_teams/view_model/view_model_competition_teams.dart'; import 'package:recording_tool/features/competition_teams/widgets/widget_manual_winner_dialog.dart'; import 'package:recording_tool/shared/widgets/app_empty_view.dart'; import 'package:recording_tool/shared/widgets/app_toast.dart'; -class CompetitionTeamDetailPage extends ConsumerWidget { - const CompetitionTeamDetailPage({ - super.key, - required this.itemId, - required this.eventId, - }); +class CompetitionTeamDetailPage extends ConsumerStatefulWidget { + const CompetitionTeamDetailPage({super.key, required this.detail}); - final String itemId; - final String eventId; + final CompetitionTeamDetail detail; @override - Widget build(BuildContext context, WidgetRef ref) { - final items = ref.watch( - competitionTeamsProvider.select((state) => state.items), + ConsumerState createState() => + _CompetitionTeamDetailPageState(); +} + +class _CompetitionTeamDetailPageState + extends ConsumerState { + /// matchupIndex -> winnerTeamId + final Map _winners = {}; + + CompetitionTeamDetail get _detail => widget.detail; + + String get _title { + final name = _detail.itemName?.trim() ?? ''; + final group = _detail.groupName?.trim() ?? ''; + if (name.isEmpty) return '参赛队伍'; + return group.isEmpty ? name : '$name ($group)'; + } + + List get _matchups => _detail.matchups ?? const []; + + CompetitionTeam _toCompetitionTeam(Team team) { + return CompetitionTeam( + id: team.id?.trim().isNotEmpty == true ? team.id! : 'unknown-team', + name: team.teamName?.trim().isNotEmpty == true ? team.teamName! : '未命名队伍', + players: (team.players ?? const []) + .map( + (player) => CompetitionPlayer( + id: player.id?.trim() ?? '', + name: player.name?.trim().isNotEmpty == true + ? player.name! + : '选手', + ), + ) + .toList(growable: false), ); - CompetitionTeamListItem? item; - for (final candidate in items) { - if (candidate.itemId == itemId) { - item = candidate; - break; - } + } + + CompetitionMatchup? _toCompetitionMatchup(Matchup matchup, int index) { + final teamA = matchup.teamA; + final teamB = matchup.teamB; + if (teamA == null || teamA.isEmpty || teamB == null || teamB.isEmpty) { + return null; } + return CompetitionMatchup( + id: '${_detail.scheduleId ?? _detail.itemId ?? 'match'}-$index', + teamA: _toCompetitionTeam(teamA.first), + teamB: _toCompetitionTeam(teamB.first), + winnerTeamId: _winners[index], + ); + } + + @override + Widget build(BuildContext context) { + final matchups = _matchups; return Scaffold( backgroundColor: Colors.white, - appBar: AppBar(title: Text(item?.title ?? '参赛队伍')), - body: item == null - ? const AppEmptyView(message: '未找到对阵信息') - : item.matchups.isEmpty + appBar: AppBar(title: Text(_title)), + body: matchups.isEmpty ? const AppEmptyView(message: '暂无对阵信息') : ListView.separated( padding: EdgeInsets.fromLTRB(20.w, 24.h, 20.w, 36.h), - itemCount: item.matchups.length, + itemCount: matchups.length, separatorBuilder: (_, _) => SizedBox(height: 22.h), itemBuilder: (context, index) { - final matchup = item!.matchups[index]; + final raw = matchups[index]; + final matchup = _toCompetitionMatchup(raw, index); + if (matchup == null) { + return const SizedBox.shrink(); + } return _MatchupCard( matchup: matchup, index: index, - onManualProcess: () => _handleManualProcess( - context, - ref, - itemId: item!.itemId, - matchup: matchup, - ), + matchTitle: raw.matchTitle?.trim() ?? '', + onManualProcess: () => _handleManualProcess(matchup, index), ); }, ), @@ -59,24 +94,16 @@ class CompetitionTeamDetailPage extends ConsumerWidget { } Future _handleManualProcess( - BuildContext context, - WidgetRef ref, { - required String itemId, - required CompetitionMatchup matchup, - }) async { + CompetitionMatchup matchup, + int index, + ) async { final winnerTeamId = await ManualWinnerDialog.show( context, matchup: matchup, ); - if (winnerTeamId == null || !context.mounted) return; + if (winnerTeamId == null || !mounted) return; - ref - .read(competitionTeamsProvider.notifier) - .selectWinner( - itemId: itemId, - matchupId: matchup.id, - winnerTeamId: winnerTeamId, - ); + setState(() => _winners[index] = winnerTeamId); final winnerName = winnerTeamId == matchup.teamA.id ? matchup.teamA.name : matchup.teamB.name; @@ -88,15 +115,18 @@ class _MatchupCard extends StatelessWidget { const _MatchupCard({ required this.matchup, required this.index, + required this.matchTitle, required this.onManualProcess, }); final CompetitionMatchup matchup; final int index; + final String matchTitle; final VoidCallback onManualProcess; @override Widget build(BuildContext context) { + final title = matchTitle.isEmpty ? '第 ${index + 1} 场' : matchTitle; return Container( key: ValueKey('competition-matchup-${matchup.id}'), padding: EdgeInsets.fromLTRB(16.w, 10.h, 16.w, 18.h), @@ -109,14 +139,17 @@ class _MatchupCard extends StatelessWidget { children: [ Row( children: [ - Text( - '第 ${index + 1} 场', - style: TextStyle( - fontSize: 14.sp, - color: const Color(0xFF7A828E), + Expanded( + child: Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF7A828E), + ), ), ), - const Spacer(), TextButton( key: ValueKey('manual-process-${matchup.id}'), onPressed: onManualProcess, diff --git a/lib/features/competition_teams/pages/page_competition_team_list.dart b/lib/features/competition_teams/pages/page_competition_team_list.dart index 668d74a..336cb95 100644 --- a/lib/features/competition_teams/pages/page_competition_team_list.dart +++ b/lib/features/competition_teams/pages/page_competition_team_list.dart @@ -4,11 +4,13 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:recording_tool/app/router/app_navigator.dart'; import 'package:recording_tool/features/competition_teams/model/model_competition_team.dart'; import 'package:recording_tool/features/competition_teams/pages/page_competition_team_detail.dart'; +import 'package:recording_tool/features/competition_teams/server/server_competition_teams.dart'; import 'package:recording_tool/features/competition_teams/view_model/view_model_competition_teams.dart'; import 'package:recording_tool/shared/widgets/app_empty_view.dart'; import 'package:recording_tool/shared/widgets/app_error_view.dart'; import 'package:recording_tool/shared/widgets/app_loading_view.dart'; import 'package:recording_tool/shared/widgets/app_refresh_list.dart'; +import 'package:recording_tool/shared/widgets/app_toast.dart'; class CompetitionTeamListPage extends ConsumerStatefulWidget { const CompetitionTeamListPage({super.key}); @@ -60,13 +62,23 @@ class _CompetitionTeamListPageState itemBuilder: (context, item, index) { return _CompetitionScheduleCard( item: item, - onTap: () => AppNavigator.push( - CompetitionTeamDetailPage( - itemId: item.itemId, - eventId: item.eventId, - ), - context: context, - ), + onTap: () async { + try { + final detail = await ref + .read(competitionTeamsServerProvider) + .fetchTeamDetail( + itemId: item.itemId, + eventId: item.eventId, + ); + if (!context.mounted) return; + AppNavigator.push( + CompetitionTeamDetailPage(detail: detail), + context: context, + ); + } catch (error) { + AppToast.show('对阵详情加载失败,请重试'); + } + }, ); }, ); diff --git a/lib/features/competition_teams/pages/page_event_team_match.dart b/lib/features/competition_teams/pages/page_event_team_match.dart index 007f785..878824c 100644 --- a/lib/features/competition_teams/pages/page_event_team_match.dart +++ b/lib/features/competition_teams/pages/page_event_team_match.dart @@ -15,12 +15,12 @@ typedef TeamQrScanner = Future Function(BuildContext context); class EventTeamMatchPage extends StatefulWidget { const EventTeamMatchPage({ super.key, - required this.item, required this.playerId, this.qrScanner, + required this.detail, }); - final EventRegistrationItem item; + final CompetitionTeamDetail detail; final String playerId; final TeamQrScanner? qrScanner; @@ -32,55 +32,113 @@ class _EventTeamMatchPageState extends State { final Set _verifiedUserIds = {}; String? _winnerTeamId; - CompetitionTeam get _homeTeam { - final leader = widget.item.teamLeader; - final teamId = leader?.userId.isNotEmpty == true - ? leader!.userId - : widget.item.userId.isNotEmpty - ? widget.item.userId - : 'home-team'; - final teamName = leader?.name.isNotEmpty == true - ? leader!.name - : widget.item.playerName.isNotEmpty - ? widget.item.playerName - : '本方队伍'; - return CompetitionTeam( - id: teamId, - name: teamName, - players: widget.item.teamMembers - .map( - (member) => CompetitionPlayer(id: member.userId, name: member.name), - ) - .toList(growable: false), - ); + CompetitionTeamDetail get _detail => widget.detail; + + /// 备注:teamA 为红队,teamB 为蓝队。 + Matchup? get _firstMatchup { + final matchups = _detail.matchups; + if (matchups == null || matchups.isEmpty) return null; + return matchups.first; } - CompetitionTeam get _opponentTeam { - final opponentId = widget.item.opponentId.isNotEmpty - ? widget.item.opponentId - : 'opponent-team'; - final opponentName = widget.item.opponentName.isNotEmpty - ? widget.item.opponentName - : '对方队伍'; - return CompetitionTeam( - id: opponentId, - name: opponentName, - players: - widget.item.opponentId.isEmpty && widget.item.opponentName.isEmpty - ? const [] - : [CompetitionPlayer(id: opponentId, name: opponentName)], - ); + /// 红队(teamA) + Team? get _redRawTeam { + final teams = _firstMatchup?.teamA; + if (teams == null || teams.isEmpty) return null; + return teams.first; } - CompetitionMatchup get _matchup => CompetitionMatchup( - id: widget.item.scheduleId.isNotEmpty - ? widget.item.scheduleId - : '${widget.item.itemId}-team-match', - teamA: _homeTeam, - teamB: _opponentTeam, - winnerTeamId: _winnerTeamId, + /// 蓝队(teamB) + Team? get _blueRawTeam { + final teams = _firstMatchup?.teamB; + if (teams == null || teams.isEmpty) return null; + return teams.first; + } + + CompetitionTeam get _redTeam => _toCompetitionTeam( + _redRawTeam, + fallbackId: 'red-team', + fallbackName: '红队', ); + CompetitionTeam get _blueTeam => _toCompetitionTeam( + _blueRawTeam, + fallbackId: 'blue-team', + fallbackName: '蓝队', + ); + + List get _redMembers => + _toEventMembers(_redRawTeam?.players); + + List get _blueMembers => + _toEventMembers(_blueRawTeam?.players); + + CompetitionMatchup get _matchup { + final scheduleId = _detail.scheduleId?.trim() ?? ''; + final itemId = _detail.itemId?.trim() ?? ''; + return CompetitionMatchup( + id: scheduleId.isNotEmpty ? scheduleId : '$itemId-team-match', + teamA: _redTeam, + teamB: _blueTeam, + winnerTeamId: _winnerTeamId, + ); + } + + /// H5 bridge 仍需要 EventRegistrationItem,从 detail 组装最小对象。 + /// 备注:红队成员写入 teamMembers,蓝队写入 opponent。 + EventRegistrationItem get _scoreBridgeItem { + final blue = _blueTeam; + return EventRegistrationItem( + eventId: _detail.eventId?.trim() ?? '', + itemId: _detail.itemId?.trim() ?? '', + scheduleId: _detail.scheduleId?.trim() ?? '', + eventName: '', + itemName: _detail.itemName?.trim() ?? '', + groupName: _detail.groupName?.trim() ?? '', + matchPlace: _detail.matchPlace?.trim() ?? '', + matchStartTime: _detail.matchStartTime?.trim() ?? '', + matchEndTime: _detail.matchEndTime?.trim() ?? '', + opponentId: blue.id == 'blue-team' ? '' : blue.id, + opponentName: blue.name == '蓝队' ? '' : blue.name, + teamMembers: _redMembers, + ); + } + + CompetitionTeam _toCompetitionTeam( + Team? team, { + required String fallbackId, + required String fallbackName, + }) { + final id = team?.id?.trim() ?? ''; + final name = team?.teamName?.trim() ?? ''; + final players = (team?.players ?? const []) + .map( + (player) => CompetitionPlayer( + id: player.id?.trim() ?? '', + name: player.name?.trim() ?? '', + ), + ) + .toList(growable: false); + return CompetitionTeam( + id: id.isNotEmpty ? id : fallbackId, + name: name.isNotEmpty ? name : fallbackName, + players: players, + ); + } + + List _toEventMembers(List? players) { + if (players == null || players.isEmpty) return const []; + return players + .map( + (player) => EventTeamMember( + userId: player.id?.trim() ?? '', + name: player.name?.trim() ?? '', + isLeader: player.isLeader ?? false, + ), + ) + .toList(growable: false); + } + @override void initState() { super.initState(); @@ -92,18 +150,15 @@ class _EventTeamMatchPageState extends State { bool _isKnownMember(String userId) { if (userId.isEmpty) return false; - if (userId == widget.item.opponentId) return true; - return widget.item.teamMembers.any((member) => member.userId == userId); + return _redMembers.any((member) => member.userId == userId) || + _blueMembers.any((member) => member.userId == userId); } String _memberNameOf(String userId) { - if (userId == widget.item.opponentId) { - return widget.item.opponentName.isEmpty - ? '对方队长' - : widget.item.opponentName; - } - for (final member in widget.item.teamMembers) { - if (member.userId == userId) return member.name; + for (final member in [..._redMembers, ..._blueMembers]) { + if (member.userId == userId) { + return member.name.isEmpty ? '参赛成员' : member.name; + } } return ''; } @@ -131,23 +186,23 @@ class _EventTeamMatchPageState extends State { ); if (!mounted || winnerTeamId == null) return; setState(() => _winnerTeamId = winnerTeamId); - final winnerName = winnerTeamId == _homeTeam.id - ? _homeTeam.name - : _opponentTeam.name; + final winnerName = winnerTeamId == _redTeam.id + ? _redTeam.name + : _blueTeam.name; AppToast.show('已设置$winnerName直接获胜'); } void _startDirectly() { AppNavigator.push( - buildTeamScorePage(item: widget.item, playerId: widget.playerId), + buildTeamScorePage(item: _scoreBridgeItem, playerId: widget.playerId), context: context, ); } @override Widget build(BuildContext context) { - final homeTeam = _homeTeam; - final opponentTeam = _opponentTeam; + final redTeam = _redTeam; + final blueTeam = _blueTeam; return Scaffold( backgroundColor: Colors.white, appBar: AppBar(), @@ -161,8 +216,16 @@ class _EventTeamMatchPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _MatchMetadata(item: widget.item), - SizedBox(height: 16.h), + _MatchMetadata(detail: _detail), + SizedBox(height: 10.h), + Text( + '备注:上方红队(teamA),下方蓝队(teamB)', + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF7A828E), + ), + ), + SizedBox(height: 8.h), Align( alignment: Alignment.centerRight, child: TextButton( @@ -179,28 +242,24 @@ class _EventTeamMatchPageState extends State { ), ), _TeamCard( - team: homeTeam, - members: widget.item.teamMembers, + teamLabel: '红队', + team: redTeam, + members: _redMembers, backgroundColor: const Color(0xFFFFEEF0), accentColor: const Color(0xFFE84B5B), verifiedUserIds: _verifiedUserIds, - winner: _winnerTeamId == homeTeam.id, + winner: _winnerTeamId == redTeam.id, ), SizedBox(height: 16.h), _TeamCard( - team: opponentTeam, - members: [ - EventTeamMember( - userId: widget.item.opponentId, - name: widget.item.opponentName, - isLeader: true, - ), - ], + teamLabel: '蓝队', + team: blueTeam, + members: _blueMembers, backgroundColor: const Color(0xFFEDF5FF), accentColor: const Color(0xFF287FDD), verifiedUserIds: _verifiedUserIds, - winner: _winnerTeamId == opponentTeam.id, - emptyMessage: '暂无对方成员数据', + winner: _winnerTeamId == blueTeam.id, + emptyMessage: '暂无蓝队成员数据', ), ], ), @@ -245,12 +304,15 @@ WebviewPage buildTeamScorePage({ } class _MatchMetadata extends StatelessWidget { - const _MatchMetadata({required this.item}); + const _MatchMetadata({required this.detail}); - final EventRegistrationItem item; + final CompetitionTeamDetail detail; @override Widget build(BuildContext context) { + final itemName = detail.itemName?.trim() ?? ''; + final matchPlace = detail.matchPlace?.trim() ?? ''; + final groupName = detail.groupName?.trim() ?? ''; return Container( width: double.infinity, padding: EdgeInsets.all(18.r), @@ -264,14 +326,14 @@ class _MatchMetadata extends StatelessWidget { Row( children: [ Expanded( - child: _MetadataText(label: '比赛项目', value: item.itemName), + child: _MetadataText(label: '比赛项目', value: itemName), ), SizedBox(width: 16.w), - _MetadataText(label: '场地', value: item.matchPlace), + _MetadataText(label: '场地', value: matchPlace), ], ), SizedBox(height: 14.h), - _MetadataText(label: '组别', value: item.groupName), + _MetadataText(label: '组别', value: groupName), ], ), ); @@ -302,6 +364,7 @@ class _MetadataText extends StatelessWidget { class _TeamCard extends StatelessWidget { const _TeamCard({ + required this.teamLabel, required this.team, required this.members, required this.backgroundColor, @@ -311,6 +374,7 @@ class _TeamCard extends StatelessWidget { this.emptyMessage = '暂无成员数据', }); + final String teamLabel; final CompetitionTeam team; final List members; final Color backgroundColor; @@ -343,7 +407,7 @@ class _TeamCard extends StatelessWidget { children: [ Expanded( child: Text( - '队长:${team.name}', + '$teamLabel:${team.name}', style: TextStyle( fontSize: 19.sp, fontWeight: FontWeight.w700, diff --git a/lib/features/competition_teams/server/server_competition_teams.dart b/lib/features/competition_teams/server/server_competition_teams.dart index 9e062bb..43caaff 100644 --- a/lib/features/competition_teams/server/server_competition_teams.dart +++ b/lib/features/competition_teams/server/server_competition_teams.dart @@ -28,13 +28,21 @@ class CompetitionTeamsServer { ); } - Future fetchTeamDetail({ + Future fetchTeamDetail({ required String itemId, required String eventId, + String? scheduleId, + String? opponentId, }) { - return _apiClient.get( + return _apiClient.post( AuthApi.getTeamDetail.path, - queryParameters: {'itemId': itemId, 'eventId': eventId}, + data: { + 'itemId': itemId, + 'eventId': eventId, + 'scheduleId': scheduleId, + 'opponentId': opponentId, + }, + parser: (json) => CompetitionTeamDetail.fromResponse(json), ); } } diff --git a/lib/features/events/pages/page_event_info.dart b/lib/features/events/pages/page_event_info.dart index d4449e2..413a35a 100644 --- a/lib/features/events/pages/page_event_info.dart +++ b/lib/features/events/pages/page_event_info.dart @@ -3,7 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:recording_tool/app/config/app_config.dart'; import 'package:recording_tool/app/router/app_navigator.dart'; +import 'package:recording_tool/features/competition_teams/model/model_competition_team.dart'; import 'package:recording_tool/features/competition_teams/pages/page_event_team_match.dart'; +import 'package:recording_tool/features/competition_teams/server/server_competition_teams.dart'; import 'package:recording_tool/features/events/model/model_event_info.dart'; import 'package:recording_tool/features/events/state/state_event_info.dart'; import 'package:recording_tool/features/events/view_model/view_model_event_info.dart'; @@ -25,13 +27,37 @@ class _EventInfoPageState extends ConsumerState { } Future onItemTap(EventRegistrationItem item) async { - // debugPrint('item tapped: ${item.itemName}'); - // await ref.read(eventInfoProvider.notifier).requestStreamKey(item); if (!mounted) return; - // final profile = ref.read(eventInfoProvider).profile; + CompetitionTeamDetail? detail; + if (item.opponentId.isNotEmpty && item.opponentId != '0') { + detail = await ref + .read(competitionTeamsServerProvider) + .fetchTeamDetail( + itemId: item.itemId, + eventId: item.eventId, + scheduleId: item.scheduleId, + opponentId: item.opponentId, + ); + if (!mounted) return; + AppNavigator.push( + buildEventRegistrationDestination( + item: item, + playerId: widget.playerId, + detail: detail, + ), + context: context, + ); + return; + } + + if (!mounted) return; AppNavigator.push( - buildEventRegistrationDestination(item: item, playerId: widget.playerId), + buildEventRegistrationDestination( + item: item, + playerId: widget.playerId, + detail: null, + ), context: context, ); } @@ -89,9 +115,10 @@ class _EventInfoPageState extends ConsumerState { Widget buildEventRegistrationDestination({ required EventRegistrationItem item, required String playerId, + required CompetitionTeamDetail? detail, }) { - if (item.opponentId.isNotEmpty && item.opponentId != '0') { - return EventTeamMatchPage(item: item, playerId: playerId); + if (detail != null) { + return EventTeamMatchPage(playerId: playerId, detail: detail); } return WebviewPage( url: AppConfig.current.mainRefereeScoreH5Url,