From 847986f861c175d057445612a935d52431f4de64 Mon Sep 17 00:00:00 2001 From: chenshuainan Date: Mon, 14 Sep 2026 09:09:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=BF=87=E6=B8=A1?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=B7=AF=E7=94=B1=E3=80=81=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=8F=8A=E5=AE=8C=E5=96=84=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为conquerPackage模块添加过渡页面路由并实现页面跳转逻辑 - 新增通用多图上传组件Upload,支持多种模式与图片操作功能 - 新增任务相关的API及Model基础文件 - 注释课前课中页面的测试试题内容 --- .../conquerPackage/beforeDuringClass/api.dart | 17 + .../beforeDuringClass/model.dart | 28 + lib/core/components/rich_text_content.dart | 40 +- lib/core/router/app_router.dart | 7 + .../beforeDuringClass/index.dart | 21 +- .../transitionPage/transition_page.dart | 703 ++++++++++++++++++ pubspec.lock | 40 + pubspec.yaml | 1 + 8 files changed, 853 insertions(+), 4 deletions(-) create mode 100644 lib/features/conquerPackage/transitionPage/transition_page.dart diff --git a/lib/core/api/conquerPackage/beforeDuringClass/api.dart b/lib/core/api/conquerPackage/beforeDuringClass/api.dart index d7903e2..47322b9 100644 --- a/lib/core/api/conquerPackage/beforeDuringClass/api.dart +++ b/lib/core/api/conquerPackage/beforeDuringClass/api.dart @@ -22,6 +22,10 @@ import 'package:new_learn/core/network/dio_client.dart'; /// —— 获取攻克报告(view 模式数据源) /// GET /api/v1/StudyPack/GetReport/{recordId} /// +/// getRecord +/// —— 获取攻克记录信息(过渡页使用) +/// GET /api/v1/StudyPack/GetRecord/{recordId} +/// /// 注意: /// - 页面只调这里,不直接碰 Dio; /// - 异常不在 catch,交页面统一处理; @@ -77,4 +81,17 @@ class BeforeDuringClassApi { } return const HandoutListResponse(); } + + /// 获取攻克记录信息 → GET /api/v1/StudyPack/GetRecord/{recordId} + /// + /// 过渡页使用:获取攻克包名称、攻克标准、是否有题目等。 + Future getRecord(String recordId) async { + final data = await DioClient.instance.get( + '/api/v1/StudyPack/GetRecord/$recordId', + ); + if (data is Map) { + return OvercomeRecordInfo.fromJson(data); + } + return const OvercomeRecordInfo(); + } } diff --git a/lib/core/api/conquerPackage/beforeDuringClass/model.dart b/lib/core/api/conquerPackage/beforeDuringClass/model.dart index a72a541..e7e89d1 100644 --- a/lib/core/api/conquerPackage/beforeDuringClass/model.dart +++ b/lib/core/api/conquerPackage/beforeDuringClass/model.dart @@ -154,6 +154,34 @@ class HandoutItem { } } +/// 攻克记录信息(过渡页使用,对标 Vue GetRecordData 返回值) +/// +/// 接口:GET /api/v1/StudyPack/GetRecord/{recordId} +/// 用于过渡页(课前/课中/课后)展示攻克包名称、攻克标准、是否有题目等。 +class OvercomeRecordInfo { + const OvercomeRecordInfo({ + this.id = '', + this.name = '', + this.overcomeStandard = 0, + this.hasQuestion = false, + }); + + final String id; // 记录 id + final String name; // 攻克包名称 + final int overcomeStandard; // 攻克标准(百分比 0-100) + final bool hasQuestion; // 是否有题目 + + factory OvercomeRecordInfo.fromJson(Map json) { + final payload = (json['data'] as Map?) ?? json; + return OvercomeRecordInfo( + id: payload['id']?.toString() ?? '', + name: payload['name'] as String? ?? '', + overcomeStandard: _parseInt(payload['overcomeStandard']), + hasQuestion: payload['hasQuestion'] as bool? ?? false, + ); + } +} + /// 讲义列表响应(Handout 接口返回的列表) class HandoutListResponse { const HandoutListResponse({this.items = const []}); diff --git a/lib/core/components/rich_text_content.dart b/lib/core/components/rich_text_content.dart index b4b25d6..d7f7b7e 100644 --- a/lib/core/components/rich_text_content.dart +++ b/lib/core/components/rich_text_content.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:new_learn/core/components/audio_player_card.dart'; import 'package:new_learn/core/components/video_player_view.dart'; @@ -79,13 +81,47 @@ class RichTextContent extends StatelessWidget { textStyle: textStyle, onTapUrl: onTapUrl ?? _defaultOnTapUrl, customStylesBuilder: customStylesBuilder, - customWidgetBuilder: _combinedCustomWidget, + customWidgetBuilder: _combinedCustomWidget(context), renderMode: renderMode, ); } /// 合并默认替换 + 用户自定义替换 - CustomWidgetBuilder get _combinedCustomWidget => (element) { + CustomWidgetBuilder _combinedCustomWidget(BuildContext context) => (element) { + // 内置: → flutter_svg 行内渲染 + // core 版 flutter_widget_from_html 不支持 SVG,手动处理 + if (element.localName == 'img') { + final src = element.attributes['src']; + if (src != null && src.toLowerCase().endsWith('.svg')) { + final width = double.tryParse(element.attributes['width'] ?? ''); + final height = double.tryParse(element.attributes['height'] ?? ''); + final w = width != null ? width.w : null; + final h = height != null ? height.h : null; + // 超采样(SSAA):SVG 以 3 倍尺寸光栅化再缩回来,消除真机边缘锯齿 + const scale = 3.0; + return InlineCustomWidget( + child: SizedBox( + width: w, + height: h, + child: OverflowBox( + maxWidth: w != null ? w * scale : double.infinity, + maxHeight: h != null ? h * scale : double.infinity, + child: Transform.scale( + scale: 1 / scale, + alignment: Alignment.center, + child: SvgPicture.network( + src, + width: w != null ? w * scale : null, + height: h != null ? h * scale : null, + fit: BoxFit.contain, + clipBehavior: Clip.antiAlias, + ), + ), + ), + ), + ); + } + } // 内置: