重构记分数据模型,替换弓型和靶纸类型为 ID 及名称,更新相关页面以支持新配置。

This commit is contained in:
2026-08-09 19:09:21 +08:00
parent e3d6fac6e1
commit 15700f918b
9 changed files with 1236 additions and 413 deletions
@@ -3,7 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:arcx/app/app_theme.dart';
import 'package:arcx/data/local/models/models.dart';
import 'package:arcx/features/scoring/application/point_book_config_provider.dart';
import 'package:arcx/features/scoring/application/scoring_service.dart';
import 'package:arcx/features/scoring/domain/point_book_config.dart';
import 'package:arcx/features/scoring/presentation/point_book_edit_page.dart';
class PointBookListPage extends ConsumerStatefulWidget {
@@ -14,22 +16,31 @@ class PointBookListPage extends ConsumerStatefulWidget {
}
class _PointBookListPageState extends ConsumerState<PointBookListPage> {
BowType? _bowFilter;
TargetFaceType? _targetFilter;
int? _bowFilter;
int? _targetFilter;
@override
Widget build(BuildContext context) {
final recordsAsync = ref.watch(pointRecordsProvider);
final configAsync = ref.watch(pointBookConfigProvider);
return Scaffold(
appBar: AppBar(title: const Text('计分记录')),
body: Column(
children: [
_FilterBar(
bowFilter: _bowFilter,
targetFilter: _targetFilter,
onBowChanged: (v) => setState(() => _bowFilter = v),
onTargetChanged: (v) => setState(() => _targetFilter = v),
configAsync.when(
loading: () => const SizedBox(height: 64),
error: (e, _) => Padding(
padding: const EdgeInsets.all(16),
child: Text('配置加载失败:$e'),
),
data: (config) => _FilterBar(
config: config,
bowFilter: _bowFilter,
targetFilter: _targetFilter,
onBowChanged: (v) => setState(() => _bowFilter = v),
onTargetChanged: (v) => setState(() => _targetFilter = v),
),
),
Expanded(
child: recordsAsync.when(
@@ -64,10 +75,10 @@ class _PointBookListPageState extends ConsumerState<PointBookListPage> {
List<PointRecord> _applyFilters(List<PointRecord> records) {
var result = records;
if (_bowFilter != null) {
result = result.where((r) => r.bowType == _bowFilter).toList();
result = result.where((r) => r.bowOptionId == _bowFilter).toList();
}
if (_targetFilter != null) {
result = result.where((r) => r.targetFaceType == _targetFilter).toList();
result = result.where((r) => r.targetOptionId == _targetFilter).toList();
}
return result;
}
@@ -107,16 +118,18 @@ class _PointBookListPageState extends ConsumerState<PointBookListPage> {
class _FilterBar extends StatelessWidget {
const _FilterBar({
required this.config,
required this.bowFilter,
required this.targetFilter,
required this.onBowChanged,
required this.onTargetChanged,
});
final BowType? bowFilter;
final TargetFaceType? targetFilter;
final ValueChanged<BowType?> onBowChanged;
final ValueChanged<TargetFaceType?> onTargetChanged;
final PointBookConfig config;
final int? bowFilter;
final int? targetFilter;
final ValueChanged<int?> onBowChanged;
final ValueChanged<int?> onTargetChanged;
@override
Widget build(BuildContext context) {
@@ -125,21 +138,23 @@ class _FilterBar extends StatelessWidget {
child: Row(
children: [
Expanded(
child: _Dropdown<BowType>(
child: _Dropdown<int>(
label: '弓型',
value: bowFilter,
items: BowType.values,
labelOf: _bowLabel,
items: config.bowOption.map((e) => e.id).toList(),
labelOf: (id) =>
config.bowById(id)?.name ?? id.toString(),
onChanged: onBowChanged,
),
),
const SizedBox(width: 12),
Expanded(
child: _Dropdown<TargetFaceType>(
child: _Dropdown<int>(
label: '靶纸',
value: targetFilter,
items: TargetFaceType.values,
labelOf: _targetLabel,
items: config.targetOption.map((e) => e.id).toList(),
labelOf: (id) =>
config.targetById(id)?.name ?? id.toString(),
onChanged: onTargetChanged,
),
),
@@ -177,10 +192,12 @@ class _Dropdown<T> extends StatelessWidget {
),
items: [
DropdownMenuItem<T>(value: null, child: Text('全部')),
...items.map((e) => DropdownMenuItem<T>(
value: e,
child: Text(labelOf(e)),
)),
...items.map(
(e) => DropdownMenuItem<T>(
value: e,
child: Text(labelOf(e), overflow: TextOverflow.ellipsis),
),
),
],
onChanged: onChanged,
);
@@ -232,7 +249,7 @@ class _RecordTile extends StatelessWidget {
],
),
subtitle: Text(
'${_bowLabel(record.bowType)} · ${record.distanceMeters}m · ${_targetLabel(record.targetFaceType)}\n'
'${record.bowName} · ${record.distanceMeters}m · ${record.targetName}\n'
'${_formatDate(record.createdAt)} ${record.totalArrows}${record.averageScore != null ? '${record.averageScore!.toStringAsFixed(1)}' : ''}',
style: TextStyle(fontSize: 12, color: AppTheme.textSecondary),
),
@@ -272,42 +289,6 @@ class _EmptyState extends StatelessWidget {
}
}
String _bowLabel(BowType t) {
switch (t) {
case BowType.recurve:
return '反曲弓';
case BowType.compound:
return '复合弓';
case BowType.barebow:
return '光弓';
case BowType.traditional:
return '传统弓';
case BowType.longbow:
return '长弓';
case BowType.other:
return '其他';
}
}
String _targetLabel(TargetFaceType t) {
switch (t) {
case TargetFaceType.cm40:
return '40cm';
case TargetFaceType.cm60:
return '60cm';
case TargetFaceType.cm80:
return '80cm';
case TargetFaceType.cm122:
return '122cm';
case TargetFaceType.vegas3Spot:
return '维加斯三联';
case TargetFaceType.vertical3Spot:
return '垂直三联';
case TargetFaceType.custom:
return '自定义';
}
}
String _formatDate(DateTime dt) {
return '${dt.year}-${dt.month.toString().padLeft(2, '0')}-${dt.day.toString().padLeft(2, '0')} '
'${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}';