Skip to content

Commit 4867edc

Browse files
committed
feat. 重构导出页,现在支持多种导出方式。
1 parent a1e65d0 commit 4867edc

7 files changed

Lines changed: 252 additions & 165 deletions

File tree

lib/DataHelper.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,9 @@ class DataHelper {
125125
/// 导出最终成绩表,给出数据库名,返回一个List<int>,即Excel文件的二进制数据
126126
/// [dbName] 数据库名
127127
/// [e] 导出类型
128-
static Future<List<int>> exportFinalScore(String dbName, ExportType e) async {
129-
var temp = await ExcelGenerator.exportScores(dbName, e);
128+
static Future<List<int>> exportFinalScore(
129+
String dbName, ExportType e, bool isContainProne) async {
130+
var temp = await ExcelGenerator.exportScores(dbName, e, isContainProne);
130131
print("All Done :D");
131132
return temp;
132133
}

lib/pageWidgets/appEntrances/exportPage.dart

Lines changed: 114 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,138 @@
1+
import 'dart:io';
2+
3+
import 'package:file_picker/file_picker.dart';
14
import 'package:flutter/material.dart';
5+
import 'package:paddle_score_app/pageWidgets/universalWidgets/ErrorHandler.dart';
6+
7+
import '../../DataHelper.dart';
8+
import '../../utils/GlobalFunction.dart';
9+
import '../universalWidgets/Loading.dart';
210

311
class ExportPage extends StatefulWidget {
4-
const ExportPage({super.key});
12+
const ExportPage({super.key, required String raceName});
513

614
@override
715
_ExportPage createState() => _ExportPage();
816
}
917

1018
class _ExportPage extends State<ExportPage> {
19+
ExportType exportType = ExportType.asDivision;
20+
21+
void handleExportTypeChange(ExportType? value) {
22+
setState(() {
23+
exportType = value!;
24+
});
25+
}
26+
27+
bool isContainPronePaddle = false;
28+
29+
void handlePronePaddleChange(bool? value) {
30+
setState(() {
31+
isContainPronePaddle = value!;
32+
});
33+
}
1134

1235
@override
1336
Widget build(BuildContext context) {
37+
final String? raceName =
38+
ModalRoute.of(context)!.settings.arguments as String?;
39+
printDebug(raceName);
1440
// TODO: implement build
1541
return Scaffold(
1642
appBar: AppBar(
1743
title: const Text('导出'),
1844
),
1945
body: Center(
2046
child: Column(
21-
mainAxisAlignment: MainAxisAlignment.center,
47+
mainAxisAlignment: MainAxisAlignment.start,
2248
children: [
23-
const Text('导出功能尚未实现'),
24-
ElevatedButton(
25-
onPressed: () {
26-
Navigator.pop(context);
27-
},
28-
child: const Text('返回'),
49+
Divider(),
50+
RadioListTile<ExportType>(
51+
title: Text('按组别导出'),
52+
value: ExportType.asDivision,
53+
groupValue: exportType,
54+
onChanged: handleExportTypeChange,
55+
),
56+
RadioListTile<ExportType>(
57+
title: Text('按代表队导出'),
58+
value: ExportType.asTeam,
59+
groupValue: exportType,
60+
onChanged: handleExportTypeChange,
61+
),
62+
// 分隔符
63+
Divider(),
64+
RadioListTile<bool>(
65+
title: Text('计算青年组趴板分数'),
66+
value: true,
67+
groupValue: isContainPronePaddle,
68+
onChanged: handlePronePaddleChange,
2969
),
70+
RadioListTile<bool>(
71+
title: Text('不计算青年组趴板分数'),
72+
value: false,
73+
groupValue: isContainPronePaddle,
74+
onChanged: handlePronePaddleChange,
75+
),
76+
Divider(),
77+
SizedBox(
78+
width: MediaQuery.of(context).size.width / 4, // 占满宽度
79+
child: TextButton(
80+
onPressed: () async {
81+
/// 点击导出
82+
try {
83+
printDebug(
84+
"选中的类型:$exportType 选中的趴板分数:$isContainPronePaddle 选中的比赛名: $raceName");
85+
var finalScoreBinary;
86+
if (exportType == ExportType.asTeam) {
87+
finalScoreBinary = await DataHelper.exportFinalScore(
88+
raceName!, ExportType.asTeam, isContainPronePaddle);
89+
} else {
90+
finalScoreBinary = await DataHelper.exportFinalScore(
91+
raceName!,
92+
ExportType.asDivision,
93+
isContainPronePaddle);
94+
}
95+
96+
/// 让用户保存文件
97+
Future.delayed(Duration.zero, () async {
98+
String? filePath = await FilePicker.platform.saveFile(
99+
dialogTitle: '保存长距离登记表',
100+
fileName: '最终成绩表 - $raceName.xlsx',
101+
);
102+
if (filePath == null) {
103+
Loading.stopLoading(context);
104+
return;
105+
}
106+
File file = File(filePath);
107+
await file.writeAsBytes(finalScoreBinary!);
108+
Loading.stopLoading(context);
109+
print("文件已保存到:$filePath");
110+
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
111+
content: Text("文件已保存到:$filePath,请检查文件是否有误")));
112+
});
113+
} catch (e) {
114+
ErrorHandler.showErrorDialog(
115+
context, "导出成绩失败,请检查所有成绩是否都已录入", e.toString());
116+
rethrow;
117+
}
118+
},
119+
style: TextButton.styleFrom(
120+
foregroundColor: Colors.white,
121+
// 文本颜色
122+
backgroundColor: Theme.of(context).primaryColor,
123+
padding: EdgeInsets.symmetric(horizontal: 16),
124+
// 水平内边距
125+
textStyle:
126+
TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
127+
// 文本样式
128+
shape: RoundedRectangleBorder(
129+
borderRadius: BorderRadius.circular(10), // 圆角半径为高度的一半
130+
),
131+
alignment: Alignment.center, // 内容左对齐
132+
),
133+
child: Text('导出'),
134+
),
135+
)
30136
],
31137
),
32138
),

lib/pageWidgets/appEntrances/racesEntrancePage.dart

Lines changed: 5 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,6 @@ class _RacePage extends State<RacePage> {
3232

3333
_RacePage(this.raceName);
3434

35-
ExportType exportType = ExportType.asDivision;
36-
37-
void handleExportTypeChange(ExportType? value) {
38-
setState(() {
39-
exportType = value!;
40-
});
41-
}
42-
43-
bool isContainPronePaddle = false;
44-
45-
void handlePronePaddleChange(bool? value) {
46-
setState(() {
47-
isContainPronePaddle = value!;
48-
});
49-
}
50-
5135
@override
5236
void initState() {
5337
super.initState();
@@ -69,7 +53,7 @@ class _RacePage extends State<RacePage> {
6953
builder: (context, snapshot) {
7054
if (snapshot.connectionState == ConnectionState.done) {
7155
return RaceNameCard(
72-
title: '6000米长距离赛(青少年3000米)',
56+
title: '长距离赛',
7357
raceName: raceName,
7458
// subtitle: "点击进入",
7559
clickable: snapshot.data as bool);
@@ -82,7 +66,7 @@ class _RacePage extends State<RacePage> {
8266
builder: (context, snapshot) {
8367
if (snapshot.connectionState == ConnectionState.done) {
8468
return RaceNameCard(
85-
title: '200米趴板划水赛(仅限青少年)',
69+
title: '趴板划水赛(仅青少年)',
8670
raceName: raceName,
8771
// subtitle: "点击进入",
8872
clickable: snapshot.data as bool);
@@ -95,7 +79,7 @@ class _RacePage extends State<RacePage> {
9579
builder: (context, snapshot) {
9680
if (snapshot.connectionState == ConnectionState.done) {
9781
return RaceNameCard(
98-
title: '500米竞速赛',
82+
title: '竞速赛',
9983
raceName: raceName,
10084
// subtitle: "点击进入",
10185
clickable: snapshot.data as bool);
@@ -123,80 +107,8 @@ class _RacePage extends State<RacePage> {
123107
borderRadius: BorderRadius.circular(12.0),
124108
onTap: () async {
125109
/// 跳转到exportPage
126-
Navigator.pushNamed(context, '/export',
127-
arguments: raceName);
128-
var choice = await showDialog<String>(
129-
context: context,
130-
barrierDismissible: false,
131-
builder: (BuildContext context) {
132-
return AlertDialog(
133-
title: const Text("请选择导出类型"),
134-
actions: <Widget>[
135-
RadioListTile<ExportType>(
136-
title: Text('按组别导出'),
137-
value: ExportType.asDivision,
138-
groupValue: exportType,
139-
onChanged: handleExportTypeChange,
140-
),
141-
RadioListTile<ExportType>(
142-
title: Text('按代表队导出'),
143-
value: ExportType.asTeam,
144-
groupValue: exportType,
145-
onChanged: handleExportTypeChange,
146-
),
147-
TextButton(
148-
onPressed: () => Navigator.pop(context, null),
149-
child: const Text('取消'),
150-
),
151-
TextButton(
152-
// 这里添加了空的Text widget
153-
onPressed: () => Navigator.pop(context, "A"),
154-
child: const Text('按组别导出'), // 补全了 child 属性
155-
),
156-
TextButton(
157-
onPressed: () => Navigator.pop(context, "B"),
158-
child: const Text('按代表队导出'),
159-
),
160-
],
161-
);
162-
},
163-
);
164-
Loading.startLoading("导出中", context);
165-
List<int> finalScoreBinary;
166-
if (choice == 'A') {
167-
finalScoreBinary = await DataHelper.exportFinalScore(
168-
raceName, ExportType.asDivision);
169-
} else if (choice == 'B') {
170-
finalScoreBinary = await DataHelper.exportFinalScore(
171-
raceName, ExportType.asTeam);
172-
} else {
173-
Loading.stopLoading(context);
174-
return;
175-
}
176-
try {
177-
await Future.delayed(Duration.zero, () async {
178-
String? filePath = await FilePicker.platform.saveFile(
179-
dialogTitle: '保存个人积分',
180-
fileName: '个人积分 - $raceName.xlsx',
181-
);
182-
if (filePath == null) {
183-
throw Exception("用户未选择文件");
184-
} else {
185-
File file = File(filePath);
186-
187-
// printDebug("!!TEST!!$choice");
188-
189-
await file.writeAsBytes(finalScoreBinary);
190-
printDebug("文件已保存到:$filePath");
191-
ScaffoldMessenger.of(context).showSnackBar(
192-
const SnackBar(content: Text('积分表下载完成')),
193-
);
194-
Loading.stopLoading(context);
195-
}
196-
});
197-
} catch (e) {
198-
Loading.stopLoading(context);
199-
}
110+
printDebug("跳转到/export/$raceName");
111+
Navigator.pushNamed(context, '/export', arguments: raceName);
200112
},
201113
child: const ListTile(
202114
title: Text('导出比赛积分表'),

lib/pageWidgets/shortDistanceRace/RaceStageCardWidget.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ class _RaceStageCardState extends State<RaceStageCard> {
124124
await FilePicker.platform
125125
.saveFile(
126126
dialogTitle:
127-
'导出${widget.division}_${widget.raceName} _${widget.stageName}分组名单(登记表)',
127+
'导出${widget.division}_${widget.raceName}_${widget.stageName}分组名单(登记表)',
128128
fileName:
129-
'${widget.division}_${widget.raceName} _${widget.stageName}成绩登记表.xlsx',
129+
'${widget.division}_${widget.raceName}_${widget.stageName}成绩登记表.xlsx',
130130
);
131131
if (filePath == null) {
132132
Loading.stopLoading(context);

lib/test/TestPage.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ class TestPage extends StatelessWidget {
204204
String dbName = "athlete";
205205
String path = "$testFilePath/最终积分表2.xlsx";
206206
var fileBinary = await DataHelper.exportFinalScore(
207-
dbName, ExportType.asDivision);
207+
dbName, ExportType.asDivision, true);
208208
File file = File(path);
209209
await file.writeAsBytes(fileBinary);
210210
print('文件已保存到: $path');

0 commit comments

Comments
 (0)