新增人工设置晋级/淘汰功能,更新相关数据模型和API接口;重构比赛页面以支持新逻辑,优化用户交互体验。

This commit is contained in:
2026-07-23 15:35:31 +08:00
parent 00937bf4b6
commit 10d9eee60e
10 changed files with 484 additions and 244 deletions
@@ -1,21 +1,31 @@
import 'package:flutter/material.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/model/model_competition_team_detail.dart';
import 'package:recording_tool/shared/widgets/app_button.dart';
/// 弹窗回传选中侧索引:0 = teamA(红队),1 = teamB(蓝队)
class ManualWinnerDialog extends StatefulWidget {
const ManualWinnerDialog({super.key, required this.matchup});
const ManualWinnerDialog({
super.key,
required this.matchup,
this.initialWinnerSideIndex,
});
final CompetitionMatchup matchup;
final Matchup matchup;
final int? initialWinnerSideIndex;
static Future<String?> show(
static Future<int?> show(
BuildContext context, {
required CompetitionMatchup matchup,
required Matchup matchup,
int? initialWinnerSideIndex,
}) {
return showDialog<String>(
return showDialog<int>(
context: context,
barrierDismissible: false,
builder: (_) => ManualWinnerDialog(matchup: matchup),
builder: (_) => ManualWinnerDialog(
matchup: matchup,
initialWinnerSideIndex: initialWinnerSideIndex,
),
);
}
@@ -24,16 +34,35 @@ class ManualWinnerDialog extends StatefulWidget {
}
class _ManualWinnerDialogState extends State<ManualWinnerDialog> {
String? _selectedTeamId;
/// 0 = teamA1 = teamB
int? _selectedSideIndex;
Team? get _teamA {
final teams = widget.matchup.teamA;
if (teams == null || teams.isEmpty) return null;
return teams.first;
}
Team? get _teamB {
final teams = widget.matchup.teamB;
if (teams == null || teams.isEmpty) return null;
return teams.first;
}
@override
void initState() {
super.initState();
_selectedTeamId = widget.matchup.winnerTeamId;
final initial = widget.initialWinnerSideIndex;
if (initial == 0 || initial == 1) {
_selectedSideIndex = initial;
}
}
@override
Widget build(BuildContext context) {
final teamA = _teamA;
final teamB = _teamB;
return AlertDialog(
insetPadding: EdgeInsets.symmetric(horizontal: 22.w),
contentPadding: EdgeInsets.zero,
@@ -65,23 +94,24 @@ class _ManualWinnerDialogState extends State<ManualWinnerDialog> {
),
),
SizedBox(height: 16.h),
_TeamChoice(
team: widget.matchup.teamA,
color: const Color(0xFFFF6B75),
selected: _selectedTeamId == widget.matchup.teamA.id,
onTap: () => setState(
() => _selectedTeamId = widget.matchup.teamA.id,
if (teamA != null) ...[
_TeamChoice(
team: teamA,
sideIndex: 0,
color: const Color(0xFFFF6B75),
selected: _selectedSideIndex == 0,
onTap: () => setState(() => _selectedSideIndex = 0),
),
),
SizedBox(height: 10.h),
_TeamChoice(
team: widget.matchup.teamB,
color: const Color(0xFF20BFA9),
selected: _selectedTeamId == widget.matchup.teamB.id,
onTap: () => setState(
() => _selectedTeamId = widget.matchup.teamB.id,
SizedBox(height: 10.h),
],
if (teamB != null)
_TeamChoice(
team: teamB,
sideIndex: 1,
color: const Color(0xFF20BFA9),
selected: _selectedSideIndex == 1,
onTap: () => setState(() => _selectedSideIndex = 1),
),
),
SizedBox(height: 20.h),
Row(
children: [
@@ -96,11 +126,11 @@ class _ManualWinnerDialogState extends State<ManualWinnerDialog> {
Expanded(
child: AppButton(
label: '确定',
onPressed: _selectedTeamId == null
onPressed: _selectedSideIndex == null
? null
: () => Navigator.of(
context,
).pop(_selectedTeamId),
).pop(_selectedSideIndex),
),
),
],
@@ -119,27 +149,33 @@ class _ManualWinnerDialogState extends State<ManualWinnerDialog> {
class _TeamChoice extends StatelessWidget {
const _TeamChoice({
required this.team,
required this.sideIndex,
required this.color,
required this.selected,
required this.onTap,
});
final CompetitionTeam team;
final Team team;
final int sideIndex;
final Color color;
final bool selected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final name = team.teamName?.trim().isNotEmpty == true
? team.teamName!
: '未命名队伍';
final players = team.playerNames;
return Semantics(
selected: selected,
button: true,
label: '选择${team.name}直接获胜',
label: '选择$name直接获胜',
child: Material(
color: color.withValues(alpha: selected ? 0.16 : 0.08),
borderRadius: BorderRadius.circular(12.r),
child: InkWell(
key: ValueKey('winner-choice-${team.id}'),
key: ValueKey('winner-choice-side-$sideIndex'),
onTap: onTap,
borderRadius: BorderRadius.circular(12.r),
child: Container(
@@ -173,21 +209,23 @@ class _TeamChoice extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
team.name,
name,
style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF20242B),
),
),
SizedBox(height: 4.h),
Text(
team.playerNames,
style: TextStyle(
fontSize: 14.sp,
color: const Color(0xFF606874),
if (players.isNotEmpty) ...[
SizedBox(height: 4.h),
Text(
players,
style: TextStyle(
fontSize: 14.sp,
color: const Color(0xFF606874),
),
),
),
],
],
),
),