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
+138 -134

No files matched your search

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