feat: 新增全局确认弹窗、多页面UI优化与网络日志开关
1. 新增全局二次确认弹窗组件confirm_box,支持自定义参数并对标小程序实现
2. 优化班级板块多处UI:
- 修复point_rank_view中Web端字体溢出问题,使用FittedBox自适应缩放
- 重构class_update_widgets的PostCard,新增卡片背景控制开关并替换光晕实现
- 优化class_update_view的在线同学栏与头像组布局,支持动态计算展示数量
- 调整my_update_detail_page的评论区结构,修复评论高亮与删除浮层定位问题
3. 为dio_client新增网络日志总开关,可按需开启请求响应打印
4. 修复多处代码格式与注释细节问题
This commit is contained in:
@@ -330,7 +330,7 @@ class _ClassUpdateViewState extends ConsumerState<ClassUpdateView> {
|
||||
return Container(
|
||||
height: 58.h,
|
||||
margin: EdgeInsets.fromLTRB(14.w, 12.h, 14.w, 15.h),
|
||||
padding: EdgeInsets.fromLTRB(2.w, 2.h, 5.w, 2.h),
|
||||
// padding: EdgeInsets.fromLTRB(2.w, 2.h, 5.w, 2.h),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: CachedNetworkImageProvider(
|
||||
@@ -339,12 +339,30 @@ class _ClassUpdateViewState extends ConsumerState<ClassUpdateView> {
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
_buildOnlineLeft(),
|
||||
const Spacer(),
|
||||
_buildAvatarStack(),
|
||||
_buildAllButton(),
|
||||
LayoutBuilder(
|
||||
builder: (context, rowConstraints) {
|
||||
final rowWidth = rowConstraints.maxWidth;
|
||||
return Row(
|
||||
children: [
|
||||
_buildOnlineLeft(),
|
||||
Expanded(
|
||||
// 占据剩余空间,把真实可用宽度传给头像组动态计算数量
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) =>
|
||||
_buildAvatarStack(constraints.maxWidth, rowWidth),
|
||||
),
|
||||
),
|
||||
// 预留「全部」按钮宽度(按钮已悬浮于右上角,不在 Row 内占位)
|
||||
SizedBox(width: 62.w),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
// 「全部」按钮:悬浮右上角(对标 Vue position: absolute; top/right: 2rpx)
|
||||
Positioned(top: 1, right: 1, child: _buildAllButton()),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -354,7 +372,38 @@ class _ClassUpdateViewState extends ConsumerState<ClassUpdateView> {
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(width: 15.w),
|
||||
const Text('在线同学', style: TextStyle(fontSize: 14, color: _titleColor)),
|
||||
// 「在线同学」底部渐变高亮条(对标 Vue 的 linear-gradient 下划线)
|
||||
// clipBehavior: Clip.none 允许渐变条溢出 Stack 边界(bottom 为负值时不被裁剪)
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Positioned(
|
||||
left: 0,
|
||||
bottom: -4,
|
||||
child: Container(
|
||||
width: 59.5.w,
|
||||
height: 9.h,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(50.r),
|
||||
gradient: const LinearGradient(
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
stops: [0, 0.3578, 1],
|
||||
colors: [
|
||||
Color(0x808AECFD), // rgba(138,236,253,0.5)
|
||||
Color(0x331D9CFF), // rgba(29,156,255,0.2)
|
||||
Color(0x003D7DF5), // rgba(61,125,245,0)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'在线同学',
|
||||
style: TextStyle(fontSize: 14, color: _titleColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 5.w),
|
||||
Text(
|
||||
'$_onlineCount',
|
||||
@@ -370,30 +419,40 @@ class _ClassUpdateViewState extends ConsumerState<ClassUpdateView> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 头像组:在线在前、离线在后,最多展示 6 个
|
||||
Widget _buildAvatarStack() {
|
||||
final urls = _avatarUrls.take(6).toList();
|
||||
if (urls.isEmpty) return const SizedBox.shrink();
|
||||
/// 头像组:在线在前、离线在后,数量按剩余宽度动态计算(对标 Vue updateAvatarMaxCount,最多 20)
|
||||
/// 可用宽度限制在右侧背景块内(对标 Vue 左区 40% + 44rpx 内边距):
|
||||
/// 左界 = 行宽 40% + 22px,右界 = 「全部」按钮左缘;同时不超过文字右侧空间,避免压住文字
|
||||
Widget _buildAvatarStack(double maxWidth, double rowWidth) {
|
||||
final size = 25.w;
|
||||
final overlap = size * 0.94;
|
||||
return SizedBox(
|
||||
width: size + (urls.length - 1) * overlap,
|
||||
height: size,
|
||||
child: Stack(
|
||||
children: [
|
||||
for (int i = 0; i < urls.length; i++)
|
||||
Positioned(
|
||||
left: i * overlap,
|
||||
child: GestureDetector(
|
||||
// 点头像 → 大图预览单张
|
||||
onTap: () => ImagePreviewViewer.showSingle(
|
||||
context,
|
||||
imageProvider: CachedNetworkImageProvider(urls[i]),
|
||||
// 0.6 行宽 − 22px 左间距 − 62px 按钮宽
|
||||
final rightBlockAvail = rowWidth * 0.6 - 84.w;
|
||||
final avail = maxWidth < rightBlockAvail ? maxWidth : rightBlockAvail;
|
||||
// 首个占满 size,后续每个只前进 overlap
|
||||
final fitCount = avail >= size ? ((avail - size) / overlap).floor() + 1 : 0;
|
||||
final urls = _avatarUrls.take(fitCount > 20 ? 20 : fitCount).toList();
|
||||
if (urls.isEmpty) return const SizedBox.shrink();
|
||||
return Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
width: size + (urls.length - 1) * overlap,
|
||||
height: size,
|
||||
child: Stack(
|
||||
children: [
|
||||
for (int i = 0; i < urls.length; i++)
|
||||
Positioned(
|
||||
left: i * overlap,
|
||||
child: GestureDetector(
|
||||
// 点头像 → 大图预览单张
|
||||
onTap: () => ImagePreviewViewer.showSingle(
|
||||
context,
|
||||
imageProvider: CachedNetworkImageProvider(urls[i]),
|
||||
),
|
||||
child: UserCircleAvatar(url: urls[i], radius: size / 2),
|
||||
),
|
||||
child: UserCircleAvatar(url: urls[i], radius: size / 2),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -404,7 +463,7 @@ class _ClassUpdateViewState extends ConsumerState<ClassUpdateView> {
|
||||
onTap: () => context.push('/class/all-student'),
|
||||
child: Container(
|
||||
width: 62.w,
|
||||
height: 34.h,
|
||||
height: 33.5.h,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
@@ -412,10 +471,32 @@ class _ClassUpdateViewState extends ConsumerState<ClassUpdateView> {
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [Color(0xFF3D7DF5), Color(0xFF3C84FF), Color(0xFF8AD1FD)],
|
||||
),
|
||||
// 对标 Vue border-radius: 0 30rpx 0 40rpx(右上 15 / 左下 20,其余直角)
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(15.r),
|
||||
bottomLeft: Radius.circular(15.r),
|
||||
bottomRight: Radius.circular(20.r),
|
||||
bottomLeft: Radius.circular(20.r),
|
||||
),
|
||||
// 对标 Vue box-shadow 外层:0 15rpx 11.3rpx rgba(61,125,245,0.08)
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x143D7DF5),
|
||||
offset: Offset(0, 7.5),
|
||||
blurRadius: 5.65,
|
||||
),
|
||||
],
|
||||
),
|
||||
// 对标 Vue box-shadow 内层 inset 0 2rpx 18.4rpx rgba(183,236,255,0.56)
|
||||
// Flutter 无内阴影,用顶部渐变近似(浅蓝光晕从顶边向下淡出)
|
||||
foregroundDecoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(15.r),
|
||||
bottomLeft: Radius.circular(20.r),
|
||||
),
|
||||
gradient: const LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
stops: [0, 0.3],
|
||||
colors: [Color(0x8FB7ECFF), Color(0x00B7ECFF)],
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
|
||||
Reference in New Issue
Block a user