feat(recite): 完成背诵模块基础框架与核心页面开发

本次提交实现了背诵模块的完整基础链路:
1. 新增词书、背单词接口层与对应数据模型
2. 完成背诵首页、章节列表页等核心页面开发
3. 实现音频播放、加载弹窗等通用组件
4. 配置路由跳转链路,替换旧版缓存传参方案
5. 对齐Vue端业务逻辑与UI样式
This commit is contained in:
崔宝新
2026-09-11 14:38:47 +08:00
parent c65df455f2
commit 27eba92315
17 changed files with 4367 additions and 36 deletions
@@ -0,0 +1,123 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
/// ============================================================
/// 背诵模块进度条组件
///
/// 1. GradientProgressBar —— 对标 Vue r-progress(linear-gradient 填充)
/// 词书卡蓝色渐变 / 关卡绿色渐变 / 未解锁灰色,全靠 colors 切换。
///
/// 2. SegmentedProgressBar —— 对标 Vue SegmentedProgressBar.vue
/// 三段独立进度,每段各自按 33/33/34 比例填充,绿色渐变。
/// ============================================================
/// 渐变线性进度条(percent:0~100)
class GradientProgressBar extends StatelessWidget {
const GradientProgressBar({
super.key,
required this.percent,
this.height = 6,
this.colors = const [Color(0xFF84AFFF), Color(0xFF3C7CF6)],
this.trackColor = const Color(0xFFF5F5F5),
this.radius,
});
/// 0~100(自动 clamp)
final double percent;
final double height;
/// 填充渐变(单色时传同色两个即可)
final List<Color> colors;
final Color trackColor;
final double? radius;
@override
Widget build(BuildContext context) {
final safe = percent.clamp(0, 100).toDouble();
return ClipRRect(
borderRadius: BorderRadius.circular(radius ?? (height / 2).r),
child: LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth;
return Container(
height: height.w,
color: trackColor,
child: Align(
alignment: Alignment.centerLeft,
child: SizedBox(
width: width * safe / 100,
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: colors,
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
),
),
),
);
},
),
);
}
}
/// 三段式分段进度条(对标 SegmentedProgressBar.vue;progress:0~100)
class SegmentedProgressBar extends StatelessWidget {
const SegmentedProgressBar({super.key, required this.progress});
final double progress;
static const Color _green1 = Color(0xFF1BC26E);
static const Color _green2 = Color(0xFF7ED8AA);
/// 与 Vue updateBarProgress 完全一致的分段算法
List<double> _segmentPercents(double p) {
double bar1;
if (p >= 33) {
bar1 = 100;
} else {
bar1 = (p / 33 * 100).clamp(0, 100).toDouble();
}
double bar2;
if (p >= 66) {
bar2 = 100;
} else if (p > 33) {
bar2 = ((p - 33) / 33 * 100).clamp(0, 100).toDouble();
} else {
bar2 = 0;
}
double bar3;
if (p >= 100) {
bar3 = 100;
} else if (p > 66) {
bar3 = ((p - 66) / 34 * 100).clamp(0, 100).toDouble();
} else {
bar3 = 0;
}
return [bar1, bar2, bar3];
}
@override
Widget build(BuildContext context) {
final segs = _segmentPercents(progress);
return Row(
children: [
for (var i = 0; i < 3; i++) ...[
if (i > 0) SizedBox(width: 5.w),
Expanded(
child: GradientProgressBar(
percent: segs[i],
height: 6,
colors: const [_green1, _green2],
),
),
],
],
);
}
}