Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4333d3bd1 | ||
|
|
e08a8ed26b | ||
|
|
62d717c3ee | ||
|
|
1f9c5af51b | ||
|
|
831ca1059b | ||
|
|
613bd90bad | ||
|
|
9818a56fe1 |
@@ -1 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
flutter build apk --release --split-per-abi
|
flutter build apk --release --split-per-abi
|
||||||
|
|
||||||
|
echo "构建完成时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
|||||||
@@ -32,17 +32,18 @@ class AppConfig {
|
|||||||
current = switch (environment) {
|
current = switch (environment) {
|
||||||
AppEnvironment.dev => const EnvironmentValues(
|
AppEnvironment.dev => const EnvironmentValues(
|
||||||
environment: AppEnvironment.dev,
|
environment: AppEnvironment.dev,
|
||||||
baseUrl: 'http://192.168.1.104:8000',
|
// baseUrl: 'http://192.168.1.104:8000',
|
||||||
|
baseUrl: 'https://apitest.dronex.cc',
|
||||||
enableNetworkLog: true,
|
enableNetworkLog: true,
|
||||||
),
|
),
|
||||||
AppEnvironment.staging => const EnvironmentValues(
|
AppEnvironment.staging => const EnvironmentValues(
|
||||||
environment: AppEnvironment.staging,
|
environment: AppEnvironment.staging,
|
||||||
baseUrl: 'https://example.com/api',
|
baseUrl: 'https://apitest.dronex.cc',
|
||||||
enableNetworkLog: true,
|
enableNetworkLog: true,
|
||||||
),
|
),
|
||||||
AppEnvironment.prod => const EnvironmentValues(
|
AppEnvironment.prod => const EnvironmentValues(
|
||||||
environment: AppEnvironment.prod,
|
environment: AppEnvironment.prod,
|
||||||
baseUrl: 'https://example.com/api',
|
baseUrl: 'https://api.dronex.cc',
|
||||||
enableNetworkLog: false,
|
enableNetworkLog: false,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class _AuthPageWidgetState extends ConsumerState<AuthPageWidget> {
|
|||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
final token = AppStorage.getString(StorageKeys.authToken);
|
final token = AppStorage.getString(StorageKeys.authToken);
|
||||||
if (token?.isNotEmpty ?? false) {
|
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);
|
state = state.copyWith(recordList: data.items);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 清空授权信息(本地 token + 内存状态)
|
||||||
|
Future<void> clearAuth() async {
|
||||||
|
await AppStorage.remove(StorageKeys.authToken);
|
||||||
|
state = const AuthState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class EventRegistrationList {
|
|||||||
return EventProfile(
|
return EventProfile(
|
||||||
name: name,
|
name: name,
|
||||||
phone: '',
|
phone: '',
|
||||||
eventTitle: items.isEmpty ? '赛事信息' : items.first.eventTitle,
|
eventTitle: items.isEmpty ? '赛事信息' : items.first.eventName,
|
||||||
avatarUrl: avatar,
|
avatarUrl: avatar,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -101,40 +101,45 @@ class EventRegistrationItem {
|
|||||||
const EventRegistrationItem({
|
const EventRegistrationItem({
|
||||||
required this.eventId,
|
required this.eventId,
|
||||||
required this.itemId,
|
required this.itemId,
|
||||||
required this.userId,
|
required this.scheduleId,
|
||||||
required this.eventTitle,
|
required this.eventName,
|
||||||
required this.name,
|
required this.itemName,
|
||||||
required this.group,
|
required this.groupName,
|
||||||
required this.venue,
|
required this.matchPlace,
|
||||||
required this.time,
|
|
||||||
required this.playerName,
|
|
||||||
required this.playerPhone,
|
|
||||||
this.avatarLabel = '头像',
|
|
||||||
this.matchStartTime = '',
|
this.matchStartTime = '',
|
||||||
this.matchEndTime = '',
|
this.matchEndTime = '',
|
||||||
this.completed = false,
|
this.completed = false,
|
||||||
this.laneNo,
|
this.opponentId = '',
|
||||||
this.status,
|
this.opponentName = '',
|
||||||
this.rawData = const {},
|
this.teamMembers,
|
||||||
|
this.userId = '',
|
||||||
|
this.playerName = '',
|
||||||
|
this.playerPhone = '',
|
||||||
});
|
});
|
||||||
|
|
||||||
final String eventId;
|
final String eventId;
|
||||||
final String itemId;
|
final String itemId;
|
||||||
final String userId;
|
final String scheduleId;
|
||||||
final String eventTitle;
|
final String eventName;
|
||||||
final String name;
|
final String itemName;
|
||||||
final String group;
|
final String groupName;
|
||||||
final String venue;
|
final String matchPlace;
|
||||||
final String time;
|
|
||||||
final String playerName;
|
|
||||||
final String playerPhone;
|
|
||||||
final String avatarLabel;
|
|
||||||
final String matchStartTime;
|
final String matchStartTime;
|
||||||
final String matchEndTime;
|
final String matchEndTime;
|
||||||
final bool completed;
|
final bool completed;
|
||||||
final String? laneNo;
|
final String opponentId;
|
||||||
final String? status;
|
final String opponentName;
|
||||||
final Map<String, dynamic> rawData;
|
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(
|
factory EventRegistrationItem.fromJson(
|
||||||
Map<dynamic, dynamic> json, {
|
Map<dynamic, dynamic> json, {
|
||||||
@@ -142,94 +147,48 @@ class EventRegistrationItem {
|
|||||||
String playerName = '',
|
String playerName = '',
|
||||||
}) {
|
}) {
|
||||||
final map = Map<String, dynamic>.from(json);
|
final map = Map<String, dynamic>.from(json);
|
||||||
final startTime = _readString(map, const ['matchStartTime', 'startTime']);
|
final startTime = _readString(map, const ['matchStartTime']);
|
||||||
final endTime = _readString(map, const ['matchEndTime', 'endTime']);
|
final endTime = _readString(map, const ['matchEndTime']);
|
||||||
final completed = _readBool(map, const ['completed']);
|
final teamMembersRaw = map['teamMembers'];
|
||||||
return EventRegistrationItem(
|
return EventRegistrationItem(
|
||||||
eventId: _readString(map, const ['eventId', 'eventsId', 'eventID']),
|
eventId: _readString(map, const ['eventId']),
|
||||||
itemId: _readString(map, const ['itemId', 'eventItemId', 'itemID']),
|
itemId: _readString(map, const ['itemId']),
|
||||||
userId: _readString(map, const [
|
scheduleId: _readString(map, const ['scheduleId']),
|
||||||
'userId',
|
eventName: _readString(map, const ['eventName'], fallback: '赛事信息'),
|
||||||
'playerId',
|
itemName: _readString(map, const ['itemName'], fallback: '未命名项目'),
|
||||||
'registrationId',
|
groupName: _readString(map, const ['groupName']),
|
||||||
], fallback: userId),
|
matchPlace: _readString(map, const ['matchPlace']),
|
||||||
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',
|
|
||||||
]),
|
|
||||||
matchStartTime: startTime,
|
matchStartTime: startTime,
|
||||||
matchEndTime: endTime,
|
matchEndTime: endTime,
|
||||||
completed: completed,
|
completed: _readBool(map, const ['completed']),
|
||||||
status: completed ? '已完成' : null,
|
opponentId: _readString(map, const ['opponentId']),
|
||||||
rawData: map,
|
opponentName: _readString(map, const ['opponentName']),
|
||||||
|
teamMembers: teamMembersRaw is List
|
||||||
|
? List<dynamic>.from(teamMembersRaw)
|
||||||
|
: null,
|
||||||
|
userId: userId,
|
||||||
|
playerName: playerName,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
EventProfile toProfile() {
|
EventProfile toProfile() {
|
||||||
return EventProfile(
|
return EventProfile(
|
||||||
avatarLabel: avatarLabel,
|
|
||||||
name: playerName,
|
name: playerName,
|
||||||
phone: playerPhone,
|
phone: playerPhone,
|
||||||
eventTitle: eventTitle,
|
eventTitle: eventName,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordingContext toRecordingContext({EventProfile? profile}) {
|
RecordingContext toRecordingContext({EventProfile? profile}) {
|
||||||
return RecordingContext(
|
return RecordingContext(
|
||||||
eventTitle: eventTitle,
|
eventTitle: eventName,
|
||||||
matchName: name,
|
matchName: itemName,
|
||||||
group: group,
|
group: groupName,
|
||||||
venue: venue,
|
venue: matchPlace,
|
||||||
time: time,
|
time: scheduleTime,
|
||||||
playerName: profile?.name ?? playerName,
|
playerName: profile?.name ?? playerName,
|
||||||
playerPhone: profile?.phone ?? playerPhone,
|
playerPhone: profile?.phone ?? playerPhone,
|
||||||
laneNo: laneNo,
|
status: statusLabel,
|
||||||
status: status,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,22 +242,13 @@ String _readString(
|
|||||||
return _readNullableString(map, keys) ?? fallback;
|
return _readNullableString(map, keys) ?? fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _readScheduleTime(
|
String _formatScheduleTime(String startTime, String endTime) {
|
||||||
Map<String, dynamic> map,
|
|
||||||
String startTime,
|
|
||||||
String endTime,
|
|
||||||
) {
|
|
||||||
if (startTime.isNotEmpty && endTime.isNotEmpty) {
|
if (startTime.isNotEmpty && endTime.isNotEmpty) {
|
||||||
return '$startTime-$endTime';
|
return '$startTime-$endTime';
|
||||||
}
|
}
|
||||||
if (startTime.isNotEmpty) return startTime;
|
if (startTime.isNotEmpty) return startTime;
|
||||||
if (endTime.isNotEmpty) return endTime;
|
if (endTime.isNotEmpty) return endTime;
|
||||||
return _readString(map, const [
|
return '';
|
||||||
'time',
|
|
||||||
'competitionTime',
|
|
||||||
'matchTime',
|
|
||||||
'scheduleTime',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int _readInt(Map<String, dynamic> map, List<String> keys, {int fallback = 0}) {
|
int _readInt(Map<String, dynamic> map, List<String> keys, {int fallback = 0}) {
|
||||||
|
|||||||
@@ -5,14 +5,12 @@ 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/model/model_event_info.dart';
|
||||||
import 'package:recording_tool/features/events/state/state_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/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';
|
import 'package:recording_tool/shared/widgets/widgets.dart';
|
||||||
|
|
||||||
class EventInfoPage extends ConsumerStatefulWidget {
|
class EventInfoPage extends ConsumerStatefulWidget {
|
||||||
const EventInfoPage({super.key, required this.playerId});
|
const EventInfoPage({super.key, required this.playerId});
|
||||||
final String playerId;
|
final String playerId;
|
||||||
static const mockRtmpUrl =
|
|
||||||
'rtmp://192.168.1.245:19090/蔡依婷vs夏志豪_空中格斗赛_高中组/蔡依婷vs夏志豪_空中格斗赛_高中组';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<EventInfoPage> createState() => _EventInfoPageState();
|
ConsumerState<EventInfoPage> createState() => _EventInfoPageState();
|
||||||
@@ -22,21 +20,20 @@ class _EventInfoPageState extends ConsumerState<EventInfoPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
if (!mounted) return;
|
|
||||||
ref.read(eventInfoProvider.notifier).loadRegistrationList();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onItemTap(EventRegistrationItem item) async {
|
Future<void> onItemTap(EventRegistrationItem item) async {
|
||||||
debugPrint('item tapped: ${item.name}');
|
// debugPrint('item tapped: ${item.itemName}');
|
||||||
await ref.read(eventInfoProvider.notifier).requestStreamKey(item);
|
// await ref.read(eventInfoProvider.notifier).requestStreamKey(item);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
final profile = ref.read(eventInfoProvider).profile;
|
// final profile = ref.read(eventInfoProvider).profile;
|
||||||
|
|
||||||
|
/// 个人赛
|
||||||
AppNavigator.push(
|
AppNavigator.push(
|
||||||
RecordingPage(
|
WebviewPage(
|
||||||
recordingContext: item.toRecordingContext(profile: profile),
|
url: 'https://drone.apptest.sportsx.cc/#/pages/h5/score-entry',
|
||||||
streamUrl: EventInfoPage.mockRtmpUrl,
|
eventRegistrationItem: item,
|
||||||
|
playerId: widget.playerId,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -179,17 +176,13 @@ class _ScheduleList extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (state.isLoading) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.items.isEmpty) {
|
if (state.items.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
state.errorMessage ?? '暂无赛事报名信息',
|
'暂无赛事报名信息',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(fontSize: 18.sp, color: const Color(0xFF2F2F2F)),
|
style: TextStyle(fontSize: 18.sp, color: const Color(0xFF2F2F2F)),
|
||||||
),
|
),
|
||||||
@@ -236,9 +229,9 @@ class _ScheduleCard extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
item.group.isEmpty
|
item.groupName.isEmpty
|
||||||
? item.name
|
? item.itemName
|
||||||
: '${item.name} (${item.group})',
|
: '${item.itemName} (${item.groupName})',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -250,7 +243,7 @@ class _ScheduleCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
SizedBox(height: 26.h),
|
SizedBox(height: 26.h),
|
||||||
Text(
|
Text(
|
||||||
item.venue,
|
item.matchPlace,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -261,7 +254,7 @@ class _ScheduleCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
SizedBox(height: 26.h),
|
SizedBox(height: 26.h),
|
||||||
Text(
|
Text(
|
||||||
item.time,
|
item.scheduleTime,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -297,7 +290,7 @@ class _ScheduleBadge extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (item.status != null) {
|
if (item.statusLabel != null) {
|
||||||
return CustomPaint(
|
return CustomPaint(
|
||||||
painter: _CutCornerBorderPainter(color: const Color(0xFF7A7A7A)),
|
painter: _CutCornerBorderPainter(color: const Color(0xFF7A7A7A)),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
@@ -305,7 +298,7 @@ class _ScheduleBadge extends StatelessWidget {
|
|||||||
height: 90.h,
|
height: 90.h,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
item.status!,
|
item.statusLabel!,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontSize: 20.sp, color: const Color(0xFF2F2F2F)),
|
style: TextStyle(fontSize: 20.sp, color: const Color(0xFF2F2F2F)),
|
||||||
@@ -316,7 +309,7 @@ class _ScheduleBadge extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Text(
|
return Text(
|
||||||
item.laneNo ?? '',
|
item.matchPlace,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
|||||||
@@ -2,40 +2,29 @@ import 'package:recording_tool/features/events/model/model_event_info.dart';
|
|||||||
|
|
||||||
class EventInfoState {
|
class EventInfoState {
|
||||||
const EventInfoState({
|
const EventInfoState({
|
||||||
this.isLoading = false,
|
|
||||||
this.isRequestingStreamKey = false,
|
this.isRequestingStreamKey = false,
|
||||||
this.errorMessage,
|
|
||||||
this.profile = const EventProfile(name: '', phone: '', eventTitle: '赛事信息'),
|
this.profile = const EventProfile(name: '', phone: '', eventTitle: '赛事信息'),
|
||||||
this.total = 0,
|
this.total = 0,
|
||||||
this.items = const [],
|
this.items = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool isLoading;
|
|
||||||
final bool isRequestingStreamKey;
|
final bool isRequestingStreamKey;
|
||||||
final String? errorMessage;
|
|
||||||
final EventProfile profile;
|
final EventProfile profile;
|
||||||
final int total;
|
final int total;
|
||||||
final List<EventRegistrationItem> items;
|
final List<EventRegistrationItem> items;
|
||||||
|
|
||||||
String get eventTitle =>
|
String get eventTitle =>
|
||||||
items.isEmpty ? profile.eventTitle : items.first.eventTitle;
|
items.isEmpty ? profile.eventTitle : items.first.eventName;
|
||||||
|
|
||||||
EventInfoState copyWith({
|
EventInfoState copyWith({
|
||||||
bool? isLoading,
|
|
||||||
bool? isRequestingStreamKey,
|
bool? isRequestingStreamKey,
|
||||||
String? errorMessage,
|
|
||||||
bool clearErrorMessage = false,
|
|
||||||
EventProfile? profile,
|
EventProfile? profile,
|
||||||
int? total,
|
int? total,
|
||||||
List<EventRegistrationItem>? items,
|
List<EventRegistrationItem>? items,
|
||||||
}) {
|
}) {
|
||||||
return EventInfoState(
|
return EventInfoState(
|
||||||
isLoading: isLoading ?? this.isLoading,
|
|
||||||
isRequestingStreamKey:
|
isRequestingStreamKey:
|
||||||
isRequestingStreamKey ?? this.isRequestingStreamKey,
|
isRequestingStreamKey ?? this.isRequestingStreamKey,
|
||||||
errorMessage: clearErrorMessage
|
|
||||||
? null
|
|
||||||
: (errorMessage ?? this.errorMessage),
|
|
||||||
profile: profile ?? this.profile,
|
profile: profile ?? this.profile,
|
||||||
total: total ?? this.total,
|
total: total ?? this.total,
|
||||||
items: items ?? this.items,
|
items: items ?? this.items,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:recording_tool/core/network/api_exception.dart';
|
|||||||
import 'package:recording_tool/features/events/model/model_event_info.dart';
|
import 'package:recording_tool/features/events/model/model_event_info.dart';
|
||||||
import 'package:recording_tool/features/events/server/server_events.dart';
|
import 'package:recording_tool/features/events/server/server_events.dart';
|
||||||
import 'package:recording_tool/features/events/state/state_event_info.dart';
|
import 'package:recording_tool/features/events/state/state_event_info.dart';
|
||||||
|
import 'package:recording_tool/shared/widgets/app_toast.dart';
|
||||||
|
|
||||||
final eventInfoProvider =
|
final eventInfoProvider =
|
||||||
StateNotifierProvider<EventInfoViewModel, EventInfoState>((ref) {
|
StateNotifierProvider<EventInfoViewModel, EventInfoState>((ref) {
|
||||||
@@ -15,44 +16,42 @@ class EventInfoViewModel extends StateNotifier<EventInfoState> {
|
|||||||
EventInfoViewModel(this._ref) : super(const EventInfoState());
|
EventInfoViewModel(this._ref) : super(const EventInfoState());
|
||||||
|
|
||||||
static const defaultRegistrationRequest = PlayerRegistrationListReq(
|
static const defaultRegistrationRequest = PlayerRegistrationListReq(
|
||||||
userId: '74300708945530955',
|
userId: '',
|
||||||
status: '',
|
status: '',
|
||||||
);
|
);
|
||||||
|
|
||||||
static const _fallbackEventId = '10224';
|
static const _fallbackEventId = '';
|
||||||
static const _fallbackItemId = '2318';
|
static const _fallbackItemId = '';
|
||||||
static const _fallbackStreamUserId = '74300708949725207';
|
static const _fallbackStreamUserId = '';
|
||||||
|
|
||||||
final Ref _ref;
|
final Ref _ref;
|
||||||
|
|
||||||
Future<void> loadRegistrationList({
|
Future<bool> loadRegistrationList({
|
||||||
PlayerRegistrationListReq request = defaultRegistrationRequest,
|
PlayerRegistrationListReq request = defaultRegistrationRequest,
|
||||||
}) async {
|
}) async {
|
||||||
state = state.copyWith(isLoading: true, clearErrorMessage: true);
|
if (request.userId.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
final result = await _ref
|
final result = await _ref
|
||||||
.read(eventsServerProvider)
|
.read(eventsServerProvider)
|
||||||
.fetchPlayerRegistrationList(request);
|
.fetchPlayerRegistrationList(request);
|
||||||
|
debugPrint('报名列表请求成功: $result');
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
isLoading: false,
|
|
||||||
profile: result.toProfile(),
|
profile: result.toProfile(),
|
||||||
total: result.total,
|
total: result.total,
|
||||||
items: result.items,
|
items: result.items,
|
||||||
clearErrorMessage: true,
|
|
||||||
);
|
);
|
||||||
} on ApiException catch (error) {
|
return true;
|
||||||
state = state.copyWith(isLoading: false, errorMessage: error.message);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
debugPrint('报名列表请求失败: $error');
|
debugPrint('报名列表请求失败: $error');
|
||||||
state = state.copyWith(isLoading: false, errorMessage: '报名列表加载失败');
|
AppToast.show('报名列表加载失败');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> requestStreamKey(EventRegistrationItem item) async {
|
Future<void> requestStreamKey(EventRegistrationItem item) async {
|
||||||
state = state.copyWith(
|
state = state.copyWith(isRequestingStreamKey: true);
|
||||||
isRequestingStreamKey: true,
|
|
||||||
clearErrorMessage: true,
|
|
||||||
);
|
|
||||||
final req = StreamKeyReq(
|
final req = StreamKeyReq(
|
||||||
eventId: item.eventId.isNotEmpty ? item.eventId : _fallbackEventId,
|
eventId: item.eventId.isNotEmpty ? item.eventId : _fallbackEventId,
|
||||||
itemId: item.itemId.isNotEmpty ? item.itemId : _fallbackItemId,
|
itemId: item.itemId.isNotEmpty ? item.itemId : _fallbackItemId,
|
||||||
@@ -64,22 +63,15 @@ class EventInfoViewModel extends StateNotifier<EventInfoState> {
|
|||||||
.read(eventsServerProvider)
|
.read(eventsServerProvider)
|
||||||
.fetchStreamKey(req);
|
.fetchStreamKey(req);
|
||||||
debugPrint('推流 Key 接口响应: $response');
|
debugPrint('推流 Key 接口响应: $response');
|
||||||
state = state.copyWith(
|
state = state.copyWith(isRequestingStreamKey: false);
|
||||||
isRequestingStreamKey: false,
|
|
||||||
clearErrorMessage: true,
|
|
||||||
);
|
|
||||||
} on ApiException catch (error) {
|
} on ApiException catch (error) {
|
||||||
debugPrint('推流 Key 接口请求失败: ${error.message}');
|
debugPrint('推流 Key 接口请求失败: ${error.message}');
|
||||||
state = state.copyWith(
|
state = state.copyWith(isRequestingStreamKey: false);
|
||||||
isRequestingStreamKey: false,
|
AppToast.show(error.message);
|
||||||
errorMessage: error.message,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
debugPrint('推流 Key 接口请求失败: $error');
|
debugPrint('推流 Key 接口请求失败: $error');
|
||||||
state = state.copyWith(
|
state = state.copyWith(isRequestingStreamKey: false);
|
||||||
isRequestingStreamKey: false,
|
AppToast.show('推流 Key 获取失败');
|
||||||
errorMessage: '推流 Key 获取失败',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:recording_tool/app/router/app_navigator.dart';
|
import 'package:recording_tool/app/router/app_navigator.dart';
|
||||||
@@ -6,7 +7,9 @@ import 'package:recording_tool/core/cache/app_storage.dart';
|
|||||||
import 'package:recording_tool/core/cache/storage_keys.dart';
|
import 'package:recording_tool/core/cache/storage_keys.dart';
|
||||||
import 'package:recording_tool/features/auth/pages/page_auth.dart';
|
import 'package:recording_tool/features/auth/pages/page_auth.dart';
|
||||||
import 'package:recording_tool/features/auth/view_model_auth/view_model_auth.dart';
|
import 'package:recording_tool/features/auth/view_model_auth/view_model_auth.dart';
|
||||||
|
import 'package:recording_tool/features/events/model/model_event_info.dart';
|
||||||
import 'package:recording_tool/features/events/pages/page_event_info.dart';
|
import 'package:recording_tool/features/events/pages/page_event_info.dart';
|
||||||
|
import 'package:recording_tool/features/events/view_model/view_model_event_info.dart';
|
||||||
import 'package:recording_tool/features/recording/model/model_recording_context.dart';
|
import 'package:recording_tool/features/recording/model/model_recording_context.dart';
|
||||||
import 'package:recording_tool/shared/widgets/app_bar.dart';
|
import 'package:recording_tool/shared/widgets/app_bar.dart';
|
||||||
import 'package:recording_tool/shared/widgets/app_button.dart';
|
import 'package:recording_tool/shared/widgets/app_button.dart';
|
||||||
@@ -54,64 +57,108 @@ class _AuthPageWidgetState extends ConsumerState<ScanQrCodePage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return PopScope(
|
||||||
appBar: myAppBar(context: context, title: '扫码'),
|
canPop: true,
|
||||||
body: Center(
|
onPopInvokedWithResult: (didPop, result) async {
|
||||||
child: Column(
|
if (!didPop) return;
|
||||||
children: [
|
await ref.read(authProvider.notifier).clearAuth();
|
||||||
SizedBox(height: 100.h),
|
},
|
||||||
Text('裁判工作台', style: TextStyle(fontSize: 30, color: Colors.black)),
|
child: Scaffold(
|
||||||
SizedBox(height: 100.h),
|
appBar: myAppBar(context: context, title: '扫码'),
|
||||||
Text(
|
body: Center(
|
||||||
'扫描选手参赛凭证进行执裁',
|
child: Column(
|
||||||
style: TextStyle(fontSize: 30, color: Colors.black),
|
children: [
|
||||||
),
|
SizedBox(height: 100.h),
|
||||||
SizedBox(height: 20.h),
|
Text(
|
||||||
|
'裁判工作台',
|
||||||
|
style: TextStyle(fontSize: 30, color: Colors.black),
|
||||||
|
),
|
||||||
|
SizedBox(height: 100.h),
|
||||||
|
Text(
|
||||||
|
'扫描选手参赛凭证进行执裁',
|
||||||
|
style: TextStyle(fontSize: 30, color: Colors.black),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20.h),
|
||||||
|
|
||||||
Consumer(
|
Consumer(
|
||||||
builder: (context, ref, child) {
|
builder: (context, ref, child) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 280.w,
|
width: 280.w,
|
||||||
height: 80.h,
|
height: 80.h,
|
||||||
child: AppButton(
|
child: AppButton(
|
||||||
label: '查看录像',
|
label: '查看录像',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final data = ref.watch(
|
final data = ref.watch(
|
||||||
authProvider.select((state) => state.jwtDecodedData),
|
authProvider.select((state) => state.jwtDecodedData),
|
||||||
);
|
);
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
debugPrint('赛事名字: ${data.eventName}');
|
debugPrint('赛事名字: ${data.eventName}');
|
||||||
final eventName = data.eventName ?? '';
|
final eventName = data.eventName ?? '';
|
||||||
await ref
|
await ref
|
||||||
.read(authProvider.notifier)
|
.read(authProvider.notifier)
|
||||||
.getRecordList(eventName);
|
.getRecordList(eventName);
|
||||||
},
|
},
|
||||||
variant: AppButtonVariant.secondary,
|
variant: AppButtonVariant.secondary,
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SizedBox(height: 16.h),
|
|
||||||
SizedBox(
|
|
||||||
width: 280.w,
|
|
||||||
height: 80.h,
|
|
||||||
child: AppButton(
|
|
||||||
label: '扫码',
|
|
||||||
onPressed: () async {
|
|
||||||
final String? playerId = await AppQrScannerDialog.show(
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
if (playerId == null || playerId.isEmpty) return;
|
|
||||||
|
|
||||||
/// TODO
|
|
||||||
AppNavigator.push(
|
|
||||||
EventInfoPage(playerId: '74300708945530955'),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
variant: AppButtonVariant.secondary,
|
|
||||||
),
|
),
|
||||||
),
|
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,
|
||||||
|
child: AppButton(
|
||||||
|
label: '扫码',
|
||||||
|
onPressed: () async {
|
||||||
|
final String? playerId = await AppQrScannerDialog.show(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
if (playerId == null || playerId.isEmpty) {
|
||||||
|
AppToast.show('查询选手信息失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EasyLoading.show(status: '查询选手信息...');
|
||||||
|
try {
|
||||||
|
final success = await ref
|
||||||
|
.read(eventInfoProvider.notifier)
|
||||||
|
.loadRegistrationList(
|
||||||
|
request: PlayerRegistrationListReq(
|
||||||
|
userId: playerId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
EasyLoading.dismiss();
|
||||||
|
if (!success) {
|
||||||
|
AppToast.show('查询选手信息失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
AppNavigator.push(EventInfoPage(playerId: playerId));
|
||||||
|
} catch (error) {
|
||||||
|
AppToast.show('查询选手信息失败');
|
||||||
|
EasyLoading.dismiss();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
variant: AppButtonVariant.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import 'dart:convert';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:recording_tool/app/router/app_navigator.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/webview_flutter.dart';
|
||||||
import 'package:webview_flutter_android/webview_flutter_android.dart';
|
import 'package:webview_flutter_android/webview_flutter_android.dart';
|
||||||
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
|
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
|
||||||
@@ -13,6 +16,8 @@ class WebviewPage extends StatefulWidget {
|
|||||||
final String? title;
|
final String? title;
|
||||||
final String? assetPath;
|
final String? assetPath;
|
||||||
final bool? canPop;
|
final bool? canPop;
|
||||||
|
final EventRegistrationItem? eventRegistrationItem;
|
||||||
|
final String? playerId;
|
||||||
|
|
||||||
const WebviewPage({
|
const WebviewPage({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -20,6 +25,8 @@ class WebviewPage extends StatefulWidget {
|
|||||||
this.title,
|
this.title,
|
||||||
this.assetPath,
|
this.assetPath,
|
||||||
this.canPop,
|
this.canPop,
|
||||||
|
this.eventRegistrationItem,
|
||||||
|
this.playerId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -57,7 +64,7 @@ class WebviewPageState extends State<WebviewPage> {
|
|||||||
late final WebViewController controller;
|
late final WebViewController controller;
|
||||||
controller = WebViewController.fromPlatformCreationParams(params)
|
controller = WebViewController.fromPlatformCreationParams(params)
|
||||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
..setBackgroundColor(Colors.transparent)
|
..setBackgroundColor(Colors.white)
|
||||||
..setNavigationDelegate(
|
..setNavigationDelegate(
|
||||||
NavigationDelegate(
|
NavigationDelegate(
|
||||||
onProgress: (int progress) {
|
onProgress: (int progress) {
|
||||||
@@ -183,8 +190,7 @@ class WebviewPageState extends State<WebviewPage> {
|
|||||||
|
|
||||||
/// 脚本注入传输给h5 数据
|
/// 脚本注入传输给h5 数据
|
||||||
Future<void> _injectH5NeedData(WebViewController controller) async {
|
Future<void> _injectH5NeedData(WebViewController controller) async {
|
||||||
final token =
|
final token = AppStorage.getString(StorageKeys.authToken);
|
||||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjgxNDI4NTczNTY2NjY4ODAwLCJkYXRhIjp7fSwic3ViIjoiODE0Mjg1NzM1NjY2Njg4MDAiLCJleHAiOjE3ODM5MzM1ODQsIm5iZiI6MTc4MzMyODc4NCwiaWF0IjoxNzgzMzI4Nzg0fQ.r1wYfvpV-5HIgwUuvq1Or3jPgjc3AhfJmoV1PNP23-Y';
|
|
||||||
try {
|
try {
|
||||||
await controller.runJavaScript('''
|
await controller.runJavaScript('''
|
||||||
(function () {
|
(function () {
|
||||||
@@ -194,6 +200,19 @@ class WebviewPageState extends State<WebviewPage> {
|
|||||||
window.__appInstallData = ${true};
|
window.__appInstallData = ${true};
|
||||||
|
|
||||||
window.AppBridge.token = '$token';
|
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('已经注入完成');
|
||||||
|
|
||||||
})();
|
})();
|
||||||
''');
|
''');
|
||||||
@@ -212,6 +231,21 @@ class WebviewPageState extends State<WebviewPage> {
|
|||||||
return const PlatformWebViewControllerCreationParams();
|
return const PlatformWebViewControllerCreationParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildWebView(WebViewController controller) {
|
||||||
|
PlatformWebViewWidgetCreationParams params =
|
||||||
|
PlatformWebViewWidgetCreationParams(controller: controller.platform);
|
||||||
|
|
||||||
|
if (WebViewPlatform.instance is AndroidWebViewPlatform) {
|
||||||
|
params =
|
||||||
|
AndroidWebViewWidgetCreationParams.fromPlatformWebViewWidgetCreationParams(
|
||||||
|
params,
|
||||||
|
displayWithHybridComposition: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WebViewWidget.fromPlatformCreationParams(params: params);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -275,7 +309,7 @@ class WebviewPageState extends State<WebviewPage> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(child: WebViewWidget(controller: controller)),
|
Positioned.fill(child: _buildWebView(controller)),
|
||||||
if (_isLoading)
|
if (_isLoading)
|
||||||
const Center(child: CircularProgressIndicator()),
|
const Center(child: CircularProgressIndicator()),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user