feat: WebView/媒体/红包详情全量实现 (#25~#30)

#25 MiniAppWebViewPage + MiniAppRouter
  - webview_flutter 加载 {apiBaseUrl}/miniapp/{appId}/index.html?gameId=...&token=...
  - MiniAppFloatButton 接收 chatId/chatType,默认打开 WebView
  - BannerState 新增 appId 字段,由 GameBannerData.appId 填充

#26 open_filex 文件打开
  - FileMessageBubble 下载完成后调用 OpenFilex.open(localPath)
  - 打开失败时 SnackBar 提示

#27 audioplayers 音频播放
  - AudioPlaybackService(Notifier):单例 AudioPlayer,togglePlay/pause/seek
  - AudioMessageBubble 接入:播放态图标切换、进度 mm:ss 显示

#28 video_player + chewie 视频全屏
  - VideoPlayerPage:本地文件 / HTTP 双模,chewie 控制栏
  - VideoMessageBubble 默认 onTap → push VideoPlayerPage

#29 红包领取排行榜详情页
  - GET /payment/rp/detail → RpDetailData + RpRecordItem DTO
  - GetRpDetailUseCase + getRpDetailUseCaseProvider
  - RedEnvelopeDetailSheet:汇总行 + 领取排行列表,头像/昵称/金额/时间

#30 MINE_RP 地雷红包发包 UI
  - _RpType 新增 mine(MINE_RP),显示地雷金额输入框
  - SendRpRequest.parameters 携带 mineAmount
  - RedEnvelopeBubble:非活跃状态直接打开详情,活跃状态领取后打开排行榜

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
pp-bot
2026-03-24 12:53:55 +09:00
parent e715a0673b
commit 23fc6b0c86
17 changed files with 1068 additions and 59 deletions

View File

@@ -10,6 +10,8 @@ import 'package:im_app/features/chat/di/red_envelope_provider.dart';
class BannerState {
final String gameId;
final String gameName;
/// 小程序 appId用于 WebView URL 构造)
final String appId;
final String? lastResult;
final BannerGameStatus status;
final int countdownSeconds;
@@ -19,6 +21,7 @@ class BannerState {
const BannerState({
this.gameId = '',
this.gameName = '',
this.appId = '',
this.lastResult,
this.status = BannerGameStatus.idle,
this.countdownSeconds = 0,
@@ -29,6 +32,7 @@ class BannerState {
BannerState copyWith({
String? gameId,
String? gameName,
String? appId,
String? lastResult,
BannerGameStatus? status,
int? countdownSeconds,
@@ -38,6 +42,7 @@ class BannerState {
return BannerState(
gameId: gameId ?? this.gameId,
gameName: gameName ?? this.gameName,
appId: appId ?? this.appId,
lastResult: lastResult ?? this.lastResult,
status: status ?? this.status,
countdownSeconds: countdownSeconds ?? this.countdownSeconds,
@@ -139,6 +144,7 @@ class BannerViewModel extends Notifier<BannerState> {
state = state.copyWith(
gameId: bean.gameId.isNotEmpty ? bean.gameId : state.gameId,
gameName: bean.gameName.isNotEmpty ? bean.gameName : state.gameName,
appId: (bean.appId != null && bean.appId!.isNotEmpty) ? bean.appId : state.appId,
lastResult: lastResult,
status: isOpen ? BannerGameStatus.open : BannerGameStatus.close,
countdownSeconds: countdown,