重构事件注册模型并更新应用配置。更改不同环境的基本url,增强事件注册项结构,并改进ScanQrCodePage中的导航。在AuthViewModel中添加clearAuth方法,以便更好地进行状态管理。
This commit is contained in:
@@ -32,17 +32,18 @@ class AppConfig {
|
||||
current = switch (environment) {
|
||||
AppEnvironment.dev => const EnvironmentValues(
|
||||
environment: AppEnvironment.dev,
|
||||
baseUrl: 'http://192.168.1.104:8000',
|
||||
// baseUrl: 'http://192.168.1.104:8000',
|
||||
baseUrl: 'https://apitest.dronex.cc',
|
||||
enableNetworkLog: true,
|
||||
),
|
||||
AppEnvironment.staging => const EnvironmentValues(
|
||||
environment: AppEnvironment.staging,
|
||||
baseUrl: 'https://example.com/api',
|
||||
baseUrl: 'https://apitest.dronex.cc',
|
||||
enableNetworkLog: true,
|
||||
),
|
||||
AppEnvironment.prod => const EnvironmentValues(
|
||||
environment: AppEnvironment.prod,
|
||||
baseUrl: 'https://example.com/api',
|
||||
baseUrl: 'https://api.dronex.cc',
|
||||
enableNetworkLog: false,
|
||||
),
|
||||
};
|
||||
|
||||
@@ -27,7 +27,7 @@ class _AuthPageWidgetState extends ConsumerState<AuthPageWidget> {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
final token = AppStorage.getString(StorageKeys.authToken);
|
||||
if (token?.isNotEmpty ?? false) {
|
||||
AppNavigator.pushReplacement(const ScanQrCodePage());
|
||||
AppNavigator.push(const ScanQrCodePage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,4 +65,10 @@ class AuthViewModel extends StateNotifier<AuthState> {
|
||||
state = state.copyWith(recordList: data.items);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 清空授权信息(本地 token + 内存状态)
|
||||
Future<void> clearAuth() async {
|
||||
await AppStorage.remove(StorageKeys.authToken);
|
||||
state = const AuthState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class EventRegistrationList {
|
||||
return EventProfile(
|
||||
name: name,
|
||||
phone: '',
|
||||
eventTitle: items.isEmpty ? '赛事信息' : items.first.eventTitle,
|
||||
eventTitle: items.isEmpty ? '赛事信息' : items.first.eventName,
|
||||
avatarUrl: avatar,
|
||||
);
|
||||
}
|
||||
@@ -101,40 +101,45 @@ class EventRegistrationItem {
|
||||
const EventRegistrationItem({
|
||||
required this.eventId,
|
||||
required this.itemId,
|
||||
required this.userId,
|
||||
required this.eventTitle,
|
||||
required this.name,
|
||||
required this.group,
|
||||
required this.venue,
|
||||
required this.time,
|
||||
required this.playerName,
|
||||
required this.playerPhone,
|
||||
this.avatarLabel = '头像',
|
||||
required this.scheduleId,
|
||||
required this.eventName,
|
||||
required this.itemName,
|
||||
required this.groupName,
|
||||
required this.matchPlace,
|
||||
this.matchStartTime = '',
|
||||
this.matchEndTime = '',
|
||||
this.completed = false,
|
||||
this.laneNo,
|
||||
this.status,
|
||||
this.rawData = const {},
|
||||
this.opponentId = '',
|
||||
this.opponentName = '',
|
||||
this.teamMembers,
|
||||
this.userId = '',
|
||||
this.playerName = '',
|
||||
this.playerPhone = '',
|
||||
});
|
||||
|
||||
final String eventId;
|
||||
final String itemId;
|
||||
final String userId;
|
||||
final String eventTitle;
|
||||
final String name;
|
||||
final String group;
|
||||
final String venue;
|
||||
final String time;
|
||||
final String playerName;
|
||||
final String playerPhone;
|
||||
final String avatarLabel;
|
||||
final String scheduleId;
|
||||
final String eventName;
|
||||
final String itemName;
|
||||
final String groupName;
|
||||
final String matchPlace;
|
||||
final String matchStartTime;
|
||||
final String matchEndTime;
|
||||
final bool completed;
|
||||
final String? laneNo;
|
||||
final String? status;
|
||||
final Map<String, dynamic> rawData;
|
||||
final String opponentId;
|
||||
final String opponentName;
|
||||
final List<dynamic>? teamMembers;
|
||||
|
||||
/// 来自报名列表父级,不在 item JSON 内
|
||||
final String userId;
|
||||
final String playerName;
|
||||
final String playerPhone;
|
||||
|
||||
String get scheduleTime =>
|
||||
_formatScheduleTime(matchStartTime, matchEndTime);
|
||||
|
||||
String? get statusLabel => completed ? '已完成' : null;
|
||||
|
||||
factory EventRegistrationItem.fromJson(
|
||||
Map<dynamic, dynamic> json, {
|
||||
@@ -142,94 +147,48 @@ class EventRegistrationItem {
|
||||
String playerName = '',
|
||||
}) {
|
||||
final map = Map<String, dynamic>.from(json);
|
||||
final startTime = _readString(map, const ['matchStartTime', 'startTime']);
|
||||
final endTime = _readString(map, const ['matchEndTime', 'endTime']);
|
||||
final completed = _readBool(map, const ['completed']);
|
||||
final startTime = _readString(map, const ['matchStartTime']);
|
||||
final endTime = _readString(map, const ['matchEndTime']);
|
||||
final teamMembersRaw = map['teamMembers'];
|
||||
return EventRegistrationItem(
|
||||
eventId: _readString(map, const ['eventId', 'eventsId', 'eventID']),
|
||||
itemId: _readString(map, const ['itemId', 'eventItemId', 'itemID']),
|
||||
userId: _readString(map, const [
|
||||
'userId',
|
||||
'playerId',
|
||||
'registrationId',
|
||||
], fallback: userId),
|
||||
eventTitle: _readString(map, const [
|
||||
'eventTitle',
|
||||
'eventName',
|
||||
'competitionName',
|
||||
'matchTitle',
|
||||
], fallback: '赛事信息'),
|
||||
name: _readString(map, const [
|
||||
'name',
|
||||
'itemName',
|
||||
'eventItemName',
|
||||
'matchName',
|
||||
'projectName',
|
||||
], fallback: '未命名项目'),
|
||||
group: _readString(map, const [
|
||||
'group',
|
||||
'groupName',
|
||||
'categoryName',
|
||||
'levelName',
|
||||
]),
|
||||
venue: _readString(map, const [
|
||||
'matchPlace',
|
||||
'venue',
|
||||
'venueName',
|
||||
'siteName',
|
||||
'fieldName',
|
||||
'placeName',
|
||||
]),
|
||||
time: _readScheduleTime(map, startTime, endTime),
|
||||
playerName: _readString(map, const [
|
||||
'playerName',
|
||||
'userName',
|
||||
'nameCn',
|
||||
'realName',
|
||||
'athleteName',
|
||||
], fallback: playerName),
|
||||
playerPhone: _readString(map, const [
|
||||
'playerPhone',
|
||||
'phone',
|
||||
'mobile',
|
||||
'telephone',
|
||||
'userPhone',
|
||||
]),
|
||||
laneNo: _readNullableString(map, const [
|
||||
'laneNo',
|
||||
'laneNumber',
|
||||
'trackNo',
|
||||
'number',
|
||||
'serialNo',
|
||||
]),
|
||||
eventId: _readString(map, const ['eventId']),
|
||||
itemId: _readString(map, const ['itemId']),
|
||||
scheduleId: _readString(map, const ['scheduleId']),
|
||||
eventName: _readString(map, const ['eventName'], fallback: '赛事信息'),
|
||||
itemName: _readString(map, const ['itemName'], fallback: '未命名项目'),
|
||||
groupName: _readString(map, const ['groupName']),
|
||||
matchPlace: _readString(map, const ['matchPlace']),
|
||||
matchStartTime: startTime,
|
||||
matchEndTime: endTime,
|
||||
completed: completed,
|
||||
status: completed ? '已完成' : null,
|
||||
rawData: map,
|
||||
completed: _readBool(map, const ['completed']),
|
||||
opponentId: _readString(map, const ['opponentId']),
|
||||
opponentName: _readString(map, const ['opponentName']),
|
||||
teamMembers: teamMembersRaw is List
|
||||
? List<dynamic>.from(teamMembersRaw)
|
||||
: null,
|
||||
userId: userId,
|
||||
playerName: playerName,
|
||||
);
|
||||
}
|
||||
|
||||
EventProfile toProfile() {
|
||||
return EventProfile(
|
||||
avatarLabel: avatarLabel,
|
||||
name: playerName,
|
||||
phone: playerPhone,
|
||||
eventTitle: eventTitle,
|
||||
eventTitle: eventName,
|
||||
);
|
||||
}
|
||||
|
||||
RecordingContext toRecordingContext({EventProfile? profile}) {
|
||||
return RecordingContext(
|
||||
eventTitle: eventTitle,
|
||||
matchName: name,
|
||||
group: group,
|
||||
venue: venue,
|
||||
time: time,
|
||||
eventTitle: eventName,
|
||||
matchName: itemName,
|
||||
group: groupName,
|
||||
venue: matchPlace,
|
||||
time: scheduleTime,
|
||||
playerName: profile?.name ?? playerName,
|
||||
playerPhone: profile?.phone ?? playerPhone,
|
||||
laneNo: laneNo,
|
||||
status: status,
|
||||
status: statusLabel,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -283,22 +242,13 @@ String _readString(
|
||||
return _readNullableString(map, keys) ?? fallback;
|
||||
}
|
||||
|
||||
String _readScheduleTime(
|
||||
Map<String, dynamic> map,
|
||||
String startTime,
|
||||
String endTime,
|
||||
) {
|
||||
String _formatScheduleTime(String startTime, String endTime) {
|
||||
if (startTime.isNotEmpty && endTime.isNotEmpty) {
|
||||
return '$startTime-$endTime';
|
||||
}
|
||||
if (startTime.isNotEmpty) return startTime;
|
||||
if (endTime.isNotEmpty) return endTime;
|
||||
return _readString(map, const [
|
||||
'time',
|
||||
'competitionTime',
|
||||
'matchTime',
|
||||
'scheduleTime',
|
||||
]);
|
||||
return '';
|
||||
}
|
||||
|
||||
int _readInt(Map<String, dynamic> map, List<String> keys, {int fallback = 0}) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'package:recording_tool/app/router/app_navigator.dart';
|
||||
import 'package:recording_tool/features/events/model/model_event_info.dart';
|
||||
import 'package:recording_tool/features/events/state/state_event_info.dart';
|
||||
import 'package:recording_tool/features/events/view_model/view_model_event_info.dart';
|
||||
import 'package:recording_tool/features/recording/pages/page_record.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_webview.dart';
|
||||
import 'package:recording_tool/shared/widgets/widgets.dart';
|
||||
|
||||
class EventInfoPage extends ConsumerStatefulWidget {
|
||||
@@ -29,14 +29,17 @@ class _EventInfoPageState extends ConsumerState<EventInfoPage> {
|
||||
}
|
||||
|
||||
Future<void> onItemTap(EventRegistrationItem item) async {
|
||||
debugPrint('item tapped: ${item.name}');
|
||||
debugPrint('item tapped: ${item.itemName}');
|
||||
await ref.read(eventInfoProvider.notifier).requestStreamKey(item);
|
||||
if (!mounted) return;
|
||||
final profile = ref.read(eventInfoProvider).profile;
|
||||
// final profile = ref.read(eventInfoProvider).profile;
|
||||
|
||||
/// 个人赛
|
||||
AppNavigator.push(
|
||||
RecordingPage(
|
||||
recordingContext: item.toRecordingContext(profile: profile),
|
||||
streamUrl: EventInfoPage.mockRtmpUrl,
|
||||
WebviewPage(
|
||||
url: 'https://drone.apptest.sportsx.cc/#/pages/h5/score-entry',
|
||||
eventRegistrationItem: item,
|
||||
playerId: widget.playerId,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -236,9 +239,9 @@ class _ScheduleCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.group.isEmpty
|
||||
? item.name
|
||||
: '${item.name} (${item.group})',
|
||||
item.groupName.isEmpty
|
||||
? item.itemName
|
||||
: '${item.itemName} (${item.groupName})',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
@@ -250,7 +253,7 @@ class _ScheduleCard extends StatelessWidget {
|
||||
),
|
||||
SizedBox(height: 26.h),
|
||||
Text(
|
||||
item.venue,
|
||||
item.matchPlace,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
@@ -261,7 +264,7 @@ class _ScheduleCard extends StatelessWidget {
|
||||
),
|
||||
SizedBox(height: 26.h),
|
||||
Text(
|
||||
item.time,
|
||||
item.scheduleTime,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
@@ -297,7 +300,7 @@ class _ScheduleBadge extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (item.status != null) {
|
||||
if (item.statusLabel != null) {
|
||||
return CustomPaint(
|
||||
painter: _CutCornerBorderPainter(color: const Color(0xFF7A7A7A)),
|
||||
child: SizedBox(
|
||||
@@ -305,7 +308,7 @@ class _ScheduleBadge extends StatelessWidget {
|
||||
height: 90.h,
|
||||
child: Center(
|
||||
child: Text(
|
||||
item.status!,
|
||||
item.statusLabel!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 20.sp, color: const Color(0xFF2F2F2F)),
|
||||
@@ -316,7 +319,7 @@ class _ScheduleBadge extends StatelessWidget {
|
||||
}
|
||||
|
||||
return Text(
|
||||
item.laneNo ?? '',
|
||||
item.matchPlace,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
|
||||
@@ -18,7 +18,7 @@ class EventInfoState {
|
||||
final List<EventRegistrationItem> items;
|
||||
|
||||
String get eventTitle =>
|
||||
items.isEmpty ? profile.eventTitle : items.first.eventTitle;
|
||||
items.isEmpty ? profile.eventTitle : items.first.eventName;
|
||||
|
||||
EventInfoState copyWith({
|
||||
bool? isLoading,
|
||||
|
||||
@@ -54,13 +54,22 @@ class _AuthPageWidgetState extends ConsumerState<ScanQrCodePage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
return PopScope(
|
||||
canPop: true,
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
if (!didPop) return;
|
||||
await ref.read(authProvider.notifier).clearAuth();
|
||||
},
|
||||
child: Scaffold(
|
||||
appBar: myAppBar(context: context, title: '扫码'),
|
||||
body: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 100.h),
|
||||
Text('裁判工作台', style: TextStyle(fontSize: 30, color: Colors.black)),
|
||||
Text(
|
||||
'裁判工作台',
|
||||
style: TextStyle(fontSize: 30, color: Colors.black),
|
||||
),
|
||||
SizedBox(height: 100.h),
|
||||
Text(
|
||||
'扫描选手参赛凭证进行执裁',
|
||||
@@ -92,6 +101,21 @@ class _AuthPageWidgetState extends ConsumerState<ScanQrCodePage> {
|
||||
},
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
Consumer(
|
||||
builder: (context, ref, child) {
|
||||
return SizedBox(
|
||||
width: 280.w,
|
||||
height: 80.h,
|
||||
child: AppButton(
|
||||
label: '参赛队伍',
|
||||
onPressed: () async {},
|
||||
variant: AppButtonVariant.secondary,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
SizedBox(
|
||||
width: 280.w,
|
||||
height: 80.h,
|
||||
@@ -114,6 +138,7 @@ class _AuthPageWidgetState extends ConsumerState<ScanQrCodePage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:recording_tool/app/router/app_navigator.dart';
|
||||
import 'package:recording_tool/core/cache/app_storage.dart';
|
||||
import 'package:recording_tool/core/cache/storage_keys.dart';
|
||||
import 'package:recording_tool/features/events/model/model_event_info.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
import 'package:webview_flutter_android/webview_flutter_android.dart';
|
||||
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
|
||||
@@ -13,6 +16,8 @@ class WebviewPage extends StatefulWidget {
|
||||
final String? title;
|
||||
final String? assetPath;
|
||||
final bool? canPop;
|
||||
final EventRegistrationItem? eventRegistrationItem;
|
||||
final String? playerId;
|
||||
|
||||
const WebviewPage({
|
||||
super.key,
|
||||
@@ -20,6 +25,8 @@ class WebviewPage extends StatefulWidget {
|
||||
this.title,
|
||||
this.assetPath,
|
||||
this.canPop,
|
||||
this.eventRegistrationItem,
|
||||
this.playerId,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -183,8 +190,7 @@ class WebviewPageState extends State<WebviewPage> {
|
||||
|
||||
/// 脚本注入传输给h5 数据
|
||||
Future<void> _injectH5NeedData(WebViewController controller) async {
|
||||
final token =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjgxNDI4NTczNTY2NjY4ODAwLCJkYXRhIjp7fSwic3ViIjoiODE0Mjg1NzM1NjY2Njg4MDAiLCJleHAiOjE3ODM5MzM1ODQsIm5iZiI6MTc4MzMyODc4NCwiaWF0IjoxNzgzMzI4Nzg0fQ.r1wYfvpV-5HIgwUuvq1Or3jPgjc3AhfJmoV1PNP23-Y';
|
||||
final token = AppStorage.getString(StorageKeys.authToken);
|
||||
try {
|
||||
await controller.runJavaScript('''
|
||||
(function () {
|
||||
@@ -194,6 +200,19 @@ class WebviewPageState extends State<WebviewPage> {
|
||||
window.__appInstallData = ${true};
|
||||
|
||||
window.AppBridge.token = '$token';
|
||||
window.AppBridge.scoreEntryContext = {
|
||||
eventId: '${widget.eventRegistrationItem?.eventId}',
|
||||
itemId: '${widget.eventRegistrationItem?.itemId}',
|
||||
scheduleId: '${widget.eventRegistrationItem?.scheduleId}',
|
||||
userId: '${widget.playerId}',
|
||||
opponentId: '${widget.eventRegistrationItem?.opponentId}',
|
||||
};
|
||||
// console.log('window.AppBridge?.scoreEntryContext.eventId', window.AppBridge?.scoreEntryContext.eventId);
|
||||
// console.log('window.AppBridge?.scoreEntryContext.itemId', window.AppBridge?.scoreEntryContext.itemId);
|
||||
// console.log('window.AppBridge?.scoreEntryContext.scheduleId', window.AppBridge?.scoreEntryContext.scheduleId);
|
||||
// console.log('window.AppBridge?.scoreEntryContext.userId', window.AppBridge?.scoreEntryContext.userId);
|
||||
// console.log('window.AppBridge?.scoreEntryContext.opponentId', window.AppBridge?.scoreEntryContext.opponentId);
|
||||
// confirm('已经注入完成');
|
||||
|
||||
})();
|
||||
''');
|
||||
|
||||
Reference in New Issue
Block a user