Skip to content

Commit fa596b1

Browse files
committed
ww
1 parent 760b67e commit fa596b1

File tree

8 files changed

+623
-100
lines changed

8 files changed

+623
-100
lines changed

.github/workflows/build.yml

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: 构建+发版
1+
name: 构建
22
on:
33
push:
44
workflow_dispatch:
@@ -31,18 +31,33 @@ jobs:
3131
- name: Upload windows artifact
3232
uses: actions/upload-artifact@v4
3333
with:
34-
name: NamePicker-action-windows
34+
name: NamePicker-Windows-x64
3535
path: |
3636
namepicker\build\windows\x64\runner\Release
37+
build_linux:
38+
runs-on: ubuntu-latest
39+
steps:
40+
- name: Clone repository
41+
uses: actions/checkout@v4
42+
43+
- name: Set up Flutter
44+
uses: subosito/flutter-action@v2
45+
with:
46+
channel: stable
47+
48+
- name: Init flutter env
49+
run: |
50+
cd namepicker
51+
flutter pub get
3752
38-
- name: Release
39-
uses: softprops/action-gh-release@v2
53+
- name: Build linux app
54+
run: |
55+
cd namepicker
56+
flutter build linux
57+
58+
- name: Upload linux artifact
59+
uses: actions/upload-artifact@v4
4060
with:
41-
name: "v3.0.0 Early Access 2 - Codename Hyacine"
42-
tag_name: "v3.0.0ea2"
43-
files: |
44-
${{ github.workspace }}\namepicker\build\windows\x64\runner\Release
45-
body_path: CHANGELOG.md
46-
prerelease: true
47-
env:
48-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61+
name: NamePicker-Linux-x64
62+
path: |
63+
namepicker\build\linux\x64\runner\Release

namepicker/lib/main.dart

Lines changed: 130 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@ import 'package:flutter/material.dart';
66
import 'package:provider/provider.dart';
77
import 'package:sprintf/sprintf.dart';
88
import 'settings_card.dart';
9+
import 'student_editor.dart';
10+
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
11+
import 'student_db.dart';
12+
import 'student.dart';
13+
import 'package:shared_preferences/shared_preferences.dart';
914

1015
// BIN 1 1111 1111 1111 0000 0000 0000 = DEC 33550336
1116
// 众人将与一人离别,惟其人将觐见奇迹
1217

1318
// 「在彩虹桥的尽头,天空之子将缝补晨昏」
14-
final version = "v3.0.0ea2";
19+
final version = "v3.0.0ea3";
1520
final codename = "Hyacine";
1621
void main() {
22+
sqfliteFfiInit();
23+
databaseFactory = databaseFactoryFfi;
1724
runApp(MyApp());
1825
}
1926

@@ -74,38 +81,118 @@ class MyApp extends StatelessWidget {
7481
}
7582

7683
class MyAppState extends ChangeNotifier {
77-
var current = "0";
84+
MyAppState() {
85+
_loadSettings();
86+
}
87+
88+
Future<void> _loadSettings() async {
89+
final prefs = await SharedPreferences.getInstance();
90+
allowRepeat = prefs.getBool('allowRepeat') ?? true;
91+
themeMode = prefs.getInt('themeMode') ?? 0;
92+
filterGender = prefs.getString('filterGender') ?? "全部";
93+
filterNumberType = prefs.getString('filterNumberType') ?? "全部";
94+
notifyListeners();
95+
}
96+
// 是否允许重复抽取
97+
bool allowRepeat = true;
98+
// 已抽过学生id列表
99+
List<int> pickedIds = [];
100+
101+
void setAllowRepeat(bool value) {
102+
allowRepeat = value;
103+
pickedIds.clear();
104+
SharedPreferences.getInstance().then((prefs) {
105+
prefs.setBool('allowRepeat', value);
106+
});
107+
notifyListeners();
108+
}
109+
var current = "别紧张...";
78110
var history = <String>[];
79111

80112
GlobalKey? historyListKey;
81113

82114
// 0: 跟随系统 1: 亮色 2: 暗色
83115
int themeMode = 0;
84-
int minValue = 0;
85-
int maxValue = 20;
116+
117+
// 筛选条件
118+
String filterGender = "全部"; // "全部" "男" "女"
119+
String filterNumberType = "全部"; // "全部" "单号" "双号"
86120

87121
void setThemeMode(int mode) {
88122
themeMode = mode;
123+
SharedPreferences.getInstance().then((prefs) {
124+
prefs.setInt('themeMode', mode);
125+
});
89126
notifyListeners();
90127
}
91128

92-
void getNext() {
93-
history.insert(0, current);
94-
var animatedList = historyListKey?.currentState as AnimatedListState?;
95-
// var names = ["sunxiaochuan","fxpick","abcdccb"];
96-
animatedList?.insertItem(0);
97-
current = sprintf("%s",[randomGen(minValue, maxValue)]);
129+
void setFilterGender(String gender) {
130+
filterGender = gender;
131+
SharedPreferences.getInstance().then((prefs) {
132+
prefs.setString('filterGender', gender);
133+
});
98134
notifyListeners();
99135
}
100136

101-
void setRange(int min, int max) {
102-
if (min > max) {
103-
final tmp = min;
104-
min = max;
105-
max = tmp;
137+
void setFilterNumberType(String type) {
138+
filterNumberType = type;
139+
SharedPreferences.getInstance().then((prefs) {
140+
prefs.setString('filterNumberType', type);
141+
});
142+
notifyListeners();
143+
}
144+
145+
Future<void> getNextStudent() async {
146+
// 获取所有学生
147+
final all = await StudentDatabase.instance.readAll();
148+
// 按性别筛选
149+
List<Student> filtered = all;
150+
if (filterGender != "全部") {
151+
filtered = filtered.where((s) => s.gender == filterGender).toList();
152+
}
153+
// 按学号单双筛选
154+
if (filterNumberType != "全部") {
155+
filtered = filtered.where((s) {
156+
final num = int.tryParse(s.studentId);
157+
if (num == null) return false;
158+
if (filterNumberType == "单号") return num % 2 == 1;
159+
if (filterNumberType == "双号") return num % 2 == 0;
160+
return true;
161+
}).toList();
162+
}
163+
// 不允许重复时,过滤已抽过
164+
if (!allowRepeat) {
165+
filtered = filtered.where((s) => !pickedIds.contains(s.id)).toList();
166+
if (filtered.isEmpty && all.isNotEmpty) {
167+
// 所有人都抽过,重置
168+
pickedIds.clear();
169+
filtered = all;
170+
if (filterGender != "全部") {
171+
filtered = filtered.where((s) => s.gender == filterGender).toList();
172+
}
173+
if (filterNumberType != "全部") {
174+
filtered = filtered.where((s) {
175+
final num = int.tryParse(s.studentId);
176+
if (num == null) return false;
177+
if (filterNumberType == "单号") return num % 2 == 1;
178+
if (filterNumberType == "双号") return num % 2 == 0;
179+
return true;
180+
}).toList();
181+
}
182+
}
106183
}
107-
minValue = min;
108-
maxValue = max;
184+
if (filtered.isEmpty) {
185+
current = "无符合条件学生";
186+
} else {
187+
final picked = filtered[Random().nextInt(filtered.length)];
188+
current = "${picked.name}(${picked.studentId})";
189+
if (!allowRepeat && picked.id != null) {
190+
pickedIds.add(picked.id!);
191+
}
192+
}
193+
history.insert(0, current);
194+
var animatedList = historyListKey?.currentState as AnimatedListState?;
195+
animatedList?.insertItem(0);
109196
notifyListeners();
110197
}
111198
}
@@ -240,31 +327,7 @@ class GeneratorPage extends StatefulWidget {
240327
}
241328

242329
class _GeneratorPageState extends State<GeneratorPage> {
243-
late TextEditingController minController;
244-
late TextEditingController maxController;
245-
246-
@override
247-
void initState() {
248-
super.initState();
249-
final appState = Provider.of<MyAppState>(context, listen: false);
250-
minController = TextEditingController(text: appState.minValue.toString());
251-
maxController = TextEditingController(text: appState.maxValue.toString());
252-
}
253-
254-
@override
255-
void didChangeDependencies() {
256-
super.didChangeDependencies();
257-
final appState = Provider.of<MyAppState>(context, listen: false);
258-
minController.text = appState.minValue.toString();
259-
maxController.text = appState.maxValue.toString();
260-
}
261-
262-
@override
263-
void dispose() {
264-
minController.dispose();
265-
maxController.dispose();
266-
super.dispose();
267-
}
330+
// 已移除范围相关控制器和生命周期方法
268331

269332
@override
270333
Widget build(BuildContext context) {
@@ -281,39 +344,22 @@ class _GeneratorPageState extends State<GeneratorPage> {
281344
SizedBox(height: 10),
282345
BigCard(pair: pair),
283346
SizedBox(height: 10),
284-
// 数字范围选择
347+
// 筛选选项
285348
Row(
286349
mainAxisAlignment: MainAxisAlignment.center,
287350
children: [
288-
Text('范围:'),
289-
SizedBox(
290-
width: 60,
291-
child: TextField(
292-
controller: minController,
293-
keyboardType: TextInputType.number,
294-
decoration: InputDecoration(
295-
labelText: '最小',
296-
),
297-
onSubmitted: (v) {
298-
final min = int.tryParse(v) ?? appState.minValue;
299-
appState.setRange(min, appState.maxValue);
300-
},
301-
),
351+
Text('性别:'),
352+
DropdownButton<String>(
353+
value: appState.filterGender,
354+
items: ['全部', '男', '女'].map((g) => DropdownMenuItem(value: g, child: Text(g))).toList(),
355+
onChanged: (v) => appState.setFilterGender(v!),
302356
),
303-
Text(' ~ '),
304-
SizedBox(
305-
width: 60,
306-
child: TextField(
307-
controller: maxController,
308-
keyboardType: TextInputType.number,
309-
decoration: InputDecoration(
310-
labelText: '最大',
311-
),
312-
onSubmitted: (v) {
313-
final max = int.tryParse(v) ?? appState.maxValue;
314-
appState.setRange(appState.minValue, max);
315-
},
316-
),
357+
SizedBox(width: 20),
358+
Text('学号类型:'),
359+
DropdownButton<String>(
360+
value: appState.filterNumberType,
361+
items: ['全部', '单号', '双号'].map((t) => DropdownMenuItem(value: t, child: Text(t))).toList(),
362+
onChanged: (v) => appState.setFilterNumberType(v!),
317363
),
318364
],
319365
),
@@ -322,15 +368,8 @@ class _GeneratorPageState extends State<GeneratorPage> {
322368
mainAxisSize: MainAxisSize.min,
323369
children: [
324370
ElevatedButton(
325-
onPressed: () {
326-
// 先同步输入框内容到状态
327-
final min = int.tryParse(minController.text) ?? appState.minValue;
328-
final max = int.tryParse(maxController.text) ?? appState.maxValue;
329-
appState.setRange(min, max);
330-
appState.getNext();
331-
// 保证输入框内容和状态同步
332-
minController.text = appState.minValue.toString();
333-
maxController.text = appState.maxValue.toString();
371+
onPressed: () async {
372+
await appState.getNextStudent();
334373
},
335374
child: Text('点击抽选'),
336375
),
@@ -387,12 +426,7 @@ class NameListPage extends StatelessWidget {
387426
Widget build(BuildContext context) {
388427
var theme = Theme.of(context);
389428
var appState = context.watch<MyAppState>();
390-
return Column(
391-
crossAxisAlignment: CrossAxisAlignment.start,
392-
children: [
393-
Placeholder()
394-
]
395-
);
429+
return StudentEditorPage();
396430
}
397431
}
398432

@@ -455,6 +489,15 @@ class SettingsPage extends StatelessWidget {
455489
],
456490
),
457491
),
492+
SettingsCard(
493+
title: Text("允许重复抽取"),
494+
leading: Icon(Icons.repeat),
495+
description: "关闭后,所有人都抽过才会重置名单",
496+
trailing: Switch(
497+
value: appState.allowRepeat,
498+
onChanged: (v) => appState.setAllowRepeat(v),
499+
),
500+
),
458501
],
459502
);
460503
}
@@ -570,3 +613,4 @@ class HistoryCard extends StatelessWidget {
570613
);
571614
}
572615
}
616+
// 文件结尾处补全所有类的闭合花括号

namepicker/lib/student.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import 'package:flutter/material.dart';
2+
3+
class Student {
4+
int? id;
5+
String name;
6+
String gender;
7+
String studentId;
8+
9+
Student({this.id, required this.name, required this.gender, required this.studentId});
10+
11+
Map<String, dynamic> toMap() {
12+
return {
13+
'id': id,
14+
'name': name,
15+
'gender': gender,
16+
'studentId': studentId,
17+
};
18+
}
19+
20+
factory Student.fromMap(Map<String, dynamic> map) {
21+
return Student(
22+
id: map['id'],
23+
name: map['name'],
24+
gender: map['gender'],
25+
studentId: map['studentId'],
26+
);
27+
}
28+
}

0 commit comments

Comments
 (0)