refactor: 重构多个页面布局与样式,修复溢出问题

调整了勘测任务页、项目页的顶部栏样式,优化了底部导航栏的底部间距
重构客户卡片布局,使用 IntrinsicHeight 避免高度溢出,添加 FittedBox 防止文本溢出
统一了部分代码的换行格式,提升代码可读性
This commit is contained in:
andyesfly committed 2026-10-02 17:54:18 +08:00
1 parent f61b7bf90b
commit e14d371cc8
4 files changed
+142 -138

No files matched your search

@@ -136,6 +136,7 @@ class _CustomerTabState extends ConsumerState<CustomerTab> {
trailingColor: AppColors.primary,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildPageHead(),
_buildSearchBar(),
@@ -297,113 +298,111 @@ class _CustomerTabState extends ConsumerState<CustomerTab> {
padding: EdgeInsets.zero,
margin: const EdgeInsets.only(bottom: 12),
onTap: () => _goDetail(c),
child: Stack(
children: [
// 左侧状态色条 / 右侧编号面板均贴边,圆角按卡片圆角裁出
Positioned(
left: 0,
top: 0,
bottom: 0,
width: 4,
child: DecoratedBox(
decoration: BoxDecoration(
color: customerStatusBarColor(c.status),
borderRadius: const BorderRadius.horizontal(
left: Radius.circular(AppRadius.lg),
// 对应源项目 `.customer-card { display:flex }`:主内容与右侧编号面板是并排的
// 两个子项,卡片高度取两者较大值。IntrinsicHeight 让两侧等高,避免右侧
// 内容(编号换行 + 操作按钮)撑出卡片导致 RenderFlex 溢出。
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 左侧状态色条,圆角按卡片圆角裁出
SizedBox(
width: 4,
child: DecoratedBox(
decoration: BoxDecoration(
color: customerStatusBarColor(c.status),
borderRadius: const BorderRadius.horizontal(
left: Radius.circular(AppRadius.lg),
),
),
),
),
),
const Positioned(
right: 0,
top: 0,
bottom: 0,
width: 86,
child: DecoratedBox(
decoration: BoxDecoration(
Expanded(
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Wrap(
spacing: 6,
runSpacing: 6,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(
c.customerName,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: AppColors.onSurface,
),
),
XChip(
customerStatusText(c.status),
tone: customerStatusTone(c.status),
),
if (c.customerLevel.isNotEmpty)
XChip(c.customerLevel, tone: ChipTone.muted),
],
),
),
const SizedBox(width: 6),
const XIcon(
'more',
size: 20,
color: AppColors.textMuted,
),
],
),
const SizedBox(height: 10),
Row(
children: [
const XIcon(
'location',
size: 16,
color: AppColors.textMuted,
),
const SizedBox(width: 4),
Text(
c.locationText,
style: const TextStyle(
fontSize: 14,
color: AppColors.textMuted,
),
),
const SizedBox(width: 16),
const XIcon(
'person',
size: 16,
color: AppColors.textMuted,
),
const SizedBox(width: 4),
Text(
c.contactPerson.isEmpty ? '-' : c.contactPerson,
style: const TextStyle(
fontSize: 14,
color: AppColors.textMuted,
),
),
],
),
],
),
),
),
// 右侧编号面板,圆角按卡片圆角裁出
Container(
width: 86,
padding: const EdgeInsets.all(12),
decoration: const BoxDecoration(
color: AppColors.surfaceBright,
borderRadius: BorderRadius.horizontal(
right: Radius.circular(AppRadius.lg),
),
),
),
),
Padding(
padding: const EdgeInsets.only(left: 4, right: 86),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Wrap(
spacing: 6,
runSpacing: 6,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(
c.customerName,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: AppColors.onSurface,
),
),
XChip(
customerStatusText(c.status),
tone: customerStatusTone(c.status),
),
if (c.customerLevel.isNotEmpty)
XChip(c.customerLevel, tone: ChipTone.muted),
],
),
),
const SizedBox(width: 6),
const XIcon('more', size: 20, color: AppColors.textMuted),
],
),
const SizedBox(height: 10),
Row(
children: [
const XIcon(
'location',
size: 16,
color: AppColors.textMuted,
),
const SizedBox(width: 4),
Text(
c.locationText,
style: const TextStyle(
fontSize: 14,
color: AppColors.textMuted,
),
),
const SizedBox(width: 16),
const XIcon('person', size: 16, color: AppColors.textMuted),
const SizedBox(width: 4),
Text(
c.contactPerson.isEmpty ? '-' : c.contactPerson,
style: const TextStyle(
fontSize: 14,
color: AppColors.textMuted,
),
),
],
),
],
),
),
),
Positioned(
right: 0,
top: 0,
bottom: 0,
width: 86,
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -432,24 +431,32 @@ class _CustomerTabState extends ConsumerState<CustomerTab> {
),
decoration: BoxDecoration(
color: const Color(0x1A712AE2),
borderRadius: BorderRadius.circular(AppRadius.regular),
borderRadius: BorderRadius.circular(
AppRadius.regular,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const XIcon(
'calendar',
size: 14,
color: AppColors.secondary,
),
const SizedBox(width: 2),
Text(
action,
style: AppTypography.captionSm.copyWith(
// '跟进展' 等较长文案在 62px 内容宽内会横向溢出,
// 等比缩放兜底,避免 RenderFlex 溢出条纹。
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const XIcon(
'calendar',
size: 14,
color: AppColors.secondary,
),
),
],
const SizedBox(width: 2),
Text(
action,
style: AppTypography.captionSm.copyWith(
color: AppColors.secondary,
),
),
],
),
),
),
),
@@ -457,9 +464,9 @@ class _CustomerTabState extends ConsumerState<CustomerTab> {
],
),
),
),
],
],
),
),
);
}
}
}
@@ -131,11 +131,9 @@ class _ProjectTabState extends ConsumerState<ProjectTab> {
});
return XPageShell(
topBar: XTopBar.brand(
title: '鑫潼智电',
trailingColor: AppColors.primary,
),
topBar: XTopBar.brand(title: '鑫潼智电', trailingColor: AppColors.primary),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildPageHead(),
_buildSearchBar(),
@@ -297,7 +295,11 @@ class _ProjectTabState extends ConsumerState<ProjectTab> {
),
),
const SizedBox(width: 12),
const XIcon('clock', size: 15, color: AppColors.textMuted),
const XIcon(
'clock',
size: 15,
color: AppColors.textMuted,
),
const SizedBox(width: 4),
Text(
formatDate(p.registerTime),
@@ -327,4 +329,4 @@ class _ProjectTabState extends ConsumerState<ProjectTab> {
),
);
}
}
}
@@ -50,7 +50,7 @@ class _SurveyListPageState extends ConsumerState<SurveyListPage> {
@override
Widget build(BuildContext context) {
return XPageShell(
topBar: XTopBar.brand(title: '勘测任务'),
topBar: XTopBar.nav(title: '勘测任务', onBack: () => context.pop()),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
@@ -156,10 +156,13 @@ class _SurveyListPageState extends ConsumerState<SurveyListPage> {
Expanded(
child: Text(
text,
style: const TextStyle(fontSize: 14, color: AppColors.onSurfaceVariant),
style: const TextStyle(
fontSize: 14,
color: AppColors.onSurfaceVariant,
),
),
),
],
);
}
}
}
+4 -12
View File
@@ -7,14 +7,10 @@ import 'x_icon.dart';
/// 底部悬浮胶囊导航,对应 `components/XBottomNav.vue`。
///
/// 距屏幕底部 12px(再叠加安全区),高 56,四周内边距 5,
/// 距屏幕底部 4px(再叠加安全区),高 56,四周内边距 5,
/// 四个 tab 等宽均分;激活项铺满所在分格。
class XBottomNav extends StatelessWidget {
const XBottomNav({
super.key,
required this.current,
required this.onChanged,
});
const XBottomNav({super.key, required this.current, required this.onChanged});
final String current;
final ValueChanged<String> onChanged;
@@ -31,11 +27,7 @@ class XBottomNav extends StatelessWidget {
final bottomInset = MediaQuery.paddingOf(context).bottom;
return Padding(
padding: EdgeInsets.only(
left: 16,
right: 16,
bottom: 12 + bottomInset,
),
padding: EdgeInsets.only(left: 16, right: 16, bottom: 4 + bottomInset),
child: Container(
height: 56,
padding: const EdgeInsets.all(5),
@@ -92,4 +84,4 @@ class XBottomNav extends StatelessWidget {
),
);
}
}
}