Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19c66efff3 | ||
|
|
9da043e68a | ||
|
|
ce89434a39 | ||
|
|
299f44fe57 | ||
|
|
d137a46e9b | ||
|
|
43373bec54 | ||
|
|
ac304b10f8 | ||
|
|
df38ecee18 | ||
|
|
7faa8e5c47 | ||
|
|
7d65be0514 |
@@ -22,6 +22,7 @@
|
||||
android:label="SportsX裁判工作台"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
|
Before Width: | Height: | Size: 251 KiB After Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
@@ -5,4 +5,4 @@ flutter build apk --release --split-per-abi
|
||||
|
||||
# echo "构建完成时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
|
||||
pgyer upload build/app/outputs/flutter-apk/app-arm64-v8a-release.apk --build-update-description "新增可视化请求插件"
|
||||
pgyer upload build/app/outputs/flutter-apk/app-arm64-v8a-release.apk --build-update-description "主裁判 APP $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
@@ -140,6 +140,7 @@ class _AuthPageWidgetState extends ConsumerState<AuthPageWidget> {
|
||||
.auth(_controller.text);
|
||||
if (!mounted) return;
|
||||
if (success) {
|
||||
_controller.clear();
|
||||
AppNavigator.push(const ScanQrCodePage());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,15 +30,20 @@ class AuthServer {
|
||||
|
||||
/// 获取赛事列表
|
||||
/// [path] 赛事目录
|
||||
/// [nasIpAddr] NAS IP,由调用方传入,避免在 authProvider 内部再 read(authProvider)。
|
||||
static Future<GetRecordListResModel?> getRecordList(
|
||||
Ref ref,
|
||||
String path,
|
||||
) async {
|
||||
Ref ref, {
|
||||
required String path,
|
||||
required String nasIpAddr,
|
||||
}) async {
|
||||
try {
|
||||
if (nasIpAddr.isEmpty) {
|
||||
throw const FormatException('无法获取NAS IP地址');
|
||||
}
|
||||
final apiClient = ref.read(apiClientProvider);
|
||||
// NAS /api/files 直接返回 {path, items},不是业务网关的 {code,message,data} 包装。
|
||||
final data = await apiClient.get<GetRecordListResModel>(
|
||||
'http://sheling.local:9001/${AuthApi.getRecordList.path}',
|
||||
'http://$nasIpAddr:9001/${AuthApi.getRecordList.path}',
|
||||
queryParameters: {'path': path},
|
||||
wrapResponse: false,
|
||||
parser: (json) {
|
||||
|
||||
@@ -71,7 +71,13 @@ class AuthViewModel extends StateNotifier<AuthState> {
|
||||
/// 获取赛事列表
|
||||
Future<bool> getRecordList(String eventName) async {
|
||||
if (eventName.isEmpty) return false;
|
||||
final data = await AuthServer.getRecordList(_ref, eventName);
|
||||
final nasIpAddr = state.jwtDecodedData?.nasIpAddr?.trim() ?? '';
|
||||
if (nasIpAddr.isEmpty) return false;
|
||||
final data = await AuthServer.getRecordList(
|
||||
_ref,
|
||||
path: eventName,
|
||||
nasIpAddr: nasIpAddr,
|
||||
);
|
||||
if (data == null) return false;
|
||||
if (data.items == null) return false;
|
||||
state = state.copyWith(recordList: data.items);
|
||||
@@ -81,7 +87,13 @@ class AuthViewModel extends StateNotifier<AuthState> {
|
||||
/// 获取指定目录的录像列表(不更新 state,用于目录下钻)
|
||||
Future<List<RecordListItem>?> fetchRecordList(String path) async {
|
||||
if (path.isEmpty) return null;
|
||||
final data = await AuthServer.getRecordList(_ref, path);
|
||||
final nasIpAddr = state.jwtDecodedData?.nasIpAddr?.trim() ?? '';
|
||||
if (nasIpAddr.isEmpty) return null;
|
||||
final data = await AuthServer.getRecordList(
|
||||
_ref,
|
||||
path: path,
|
||||
nasIpAddr: nasIpAddr,
|
||||
);
|
||||
return data?.items;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,6 +127,8 @@ class EventRegistrationItem {
|
||||
required this.itemName,
|
||||
required this.groupName,
|
||||
required this.matchPlace,
|
||||
required this.allowByeMatch,
|
||||
|
||||
this.matchStartTime = '',
|
||||
this.matchEndTime = '',
|
||||
this.completed = false,
|
||||
@@ -136,6 +138,7 @@ class EventRegistrationItem {
|
||||
this.userId = '',
|
||||
this.playerName = '',
|
||||
this.playerPhone = '',
|
||||
this.playerNo = '',
|
||||
});
|
||||
|
||||
final String eventId;
|
||||
@@ -151,6 +154,8 @@ class EventRegistrationItem {
|
||||
final String opponentId;
|
||||
final String opponentName;
|
||||
final List<EventTeamMember> teamMembers;
|
||||
final String playerNo;
|
||||
final int allowByeMatch;
|
||||
|
||||
/// 来自报名列表父级,不在 item JSON 内
|
||||
final String userId;
|
||||
@@ -198,6 +203,8 @@ class EventRegistrationItem {
|
||||
: const [],
|
||||
userId: userId,
|
||||
playerName: playerName,
|
||||
playerNo: _readString(map, const ['playerNo']),
|
||||
allowByeMatch: _readInt(map, const ['allowByeMatch']),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,16 @@ class _EventInfoPageState extends ConsumerState<EventInfoPage> {
|
||||
if (!mounted) return;
|
||||
CompetitionTeamDetail? detail;
|
||||
if (item.opponentId.isNotEmpty && item.opponentId != '0') {
|
||||
// allowByeMatch = 0 (直接晋级)
|
||||
// allowByeMatch = 1 (和败方对战)
|
||||
// allowByeMatch = -1 (个人赛不适用此配置,所以 = -1)
|
||||
// 如果比赛已结束 点击反应
|
||||
if (item.completed) return;
|
||||
final opponent = item.opponentName.trim();
|
||||
if (opponent.isEmpty &&
|
||||
(item.allowByeMatch == 0 || item.allowByeMatch == 1)) {
|
||||
return;
|
||||
}
|
||||
detail = await ref
|
||||
.read(competitionTeamsServerProvider)
|
||||
.fetchTeamDetail(
|
||||
@@ -51,6 +61,15 @@ class _EventInfoPageState extends ConsumerState<EventInfoPage> {
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await ref
|
||||
.read(eventInfoProvider.notifier)
|
||||
.loadRegistrationList(
|
||||
request: PlayerRegistrationListReq(userId: widget.playerId),
|
||||
);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
@@ -418,7 +437,12 @@ class _ScheduleDetails extends StatelessWidget {
|
||||
],
|
||||
if (opponentLine.isNotEmpty) ...[
|
||||
SizedBox(height: 3.h),
|
||||
_MetaText(opponentLine),
|
||||
Row(
|
||||
children: [
|
||||
_MetaText('对手:'),
|
||||
_MetaText(opponentLine, color: _opponentLineColor(opponentLine)),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
@@ -426,10 +450,12 @@ class _ScheduleDetails extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _MetaText extends StatelessWidget {
|
||||
const _MetaText(this.text);
|
||||
const _MetaText(this.text, {this.color = const Color(0xFF6E747D)});
|
||||
|
||||
final String text;
|
||||
|
||||
final Color color;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
@@ -439,7 +465,7 @@ class _MetaText extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 10.sp,
|
||||
height: 1.2,
|
||||
color: const Color(0xFF6E747D),
|
||||
color: color,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
);
|
||||
@@ -516,14 +542,26 @@ String _formatMemberLine(EventRegistrationItem item) {
|
||||
|
||||
String _formatOpponentLine(EventRegistrationItem item) {
|
||||
final opponent = item.opponentName.trim();
|
||||
if (opponent.isEmpty) return '';
|
||||
return '对手:$opponent';
|
||||
if (opponent.isEmpty && item.allowByeMatch == 0) {
|
||||
return item.completed ? '轮空' : '';
|
||||
}
|
||||
if (opponent.isEmpty && item.allowByeMatch == 1) {
|
||||
return '待分配';
|
||||
}
|
||||
return opponent;
|
||||
}
|
||||
|
||||
Color _opponentLineColor(String opponentLine) {
|
||||
if (opponentLine.contains('待分配')) return const Color(0xFFF1C585);
|
||||
if (opponentLine.contains('轮空')) return const Color(0xFF41BFBF);
|
||||
return const Color(0xFF6E747D);
|
||||
}
|
||||
|
||||
String _formatNumberBadge(EventRegistrationItem item) {
|
||||
final place = item.matchPlace.trim();
|
||||
if (place.isEmpty) return '';
|
||||
final match = RegExp(r'\d+').firstMatch(place);
|
||||
if (match != null) return '${match.group(0)}号';
|
||||
return place;
|
||||
return item.playerNo.trim().isEmpty ? '' : '${item.playerNo.trim()}号';
|
||||
// final place = item.matchPlace.trim();
|
||||
// if (place.isEmpty) return '';
|
||||
// final match = RegExp(r'\d+').firstMatch(place);
|
||||
// if (match != null) return '${match.group(0)}号';
|
||||
// return place;
|
||||
}
|
||||
|
||||