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, + ), + ), + ), + ), + ); + } + } // 内置: