Files
jiu/client/lib/widgets/mobile_list_card.dart
T
wangjia d1b7e8e2e9 refactor(client): 前端体系治理——漏斗退役/老卡片标 legacy/fidelity 收阈/文档口径修正
- DsFilterHeader 删除(筛选已全部上移工具栏 chips,屏内零引用)
- mobile_list_card 标 LEGACY 禁新用(6 屏迁移 MCard 登记 todo #13)
- fidelity 阈值按 2026-07-07 实测收紧:一刀切 8% → 各屏实测最大残差+2pp
  (4%~7%),新阈下 36 屏×主题全过;proto 基准同步重摄(含库存改版)
- CLAUDE.md 修正:表格筛选口径(工具栏 chips+列头仅排序)、响应式导航
  (Drawer 退役→底部 5 tab,修文字滞后)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
2026-07-07 18:24:48 +08:00

134 lines
4.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import '../core/theme/context_tokens.dart';
// ⚠️ LEGACY2026-07-07 标注):本组件为旧移动卡片范式(label:value 竖排字段),
// 新范式 = widgets/ds/m_card.dart 的 MCard(镜像原型 mobile-atoms .m-card
// 图标徽章 + 双栏紧凑)。**新屏禁用本组件,一律用 MCard**;存量 6 屏
// (出入库录单/往来/盘点/基础数据/财务)迁移已登记 todo,迁完删除本文件。
/// 移动端列表卡片的单个字段(label: value)。
class MobileCardField {
final String label;
final String? value;
/// 自定义值控件(如带颜色的金额、状态徽章);提供后忽略 [value]。
final Widget? valueWidget;
const MobileCardField(this.label, this.value, {this.valueWidget});
}
/// 窄屏(手机)列表的通用卡片:标题 + 右上角徽章 + 字段竖排 + 底部操作。
/// 替代宽屏的表格行,使一行数据在手机上可一屏读完、操作可点。
class MobileListCard extends StatelessWidget {
final Widget title;
final Widget? subtitle;
final Widget? trailing;
final List<MobileCardField> fields;
final List<Widget>? actions;
final VoidCallback? onTap;
const MobileListCard({
super.key,
required this.title,
this.subtitle,
this.trailing,
this.fields = const [],
this.actions,
this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.zero,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: context.tokens.borderSubtle),
),
child: InkWell(
borderRadius: BorderRadius.circular(8),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DefaultTextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: context.tokens.text,
),
child: title,
),
if (subtitle != null) ...[
const SizedBox(height: 2),
DefaultTextStyle(
style: TextStyle(
fontSize: 12, color: context.tokens.muted),
child: subtitle!,
),
],
],
),
),
if (trailing != null) ...[
const SizedBox(width: 8),
trailing!,
],
],
),
if (fields.isNotEmpty) ...[
const SizedBox(height: 10),
...fields.map((f) => _buildField(context, f)),
],
if (actions != null && actions!.isNotEmpty) ...[
const Divider(height: 18),
Align(
alignment: Alignment.centerRight,
child: Wrap(
spacing: 4,
children: actions!,
),
),
],
],
),
),
),
);
}
Widget _buildField(BuildContext context, MobileCardField f) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 64,
child: Text(
f.label,
style: TextStyle(fontSize: 13, color: context.tokens.muted),
),
),
Expanded(
child: f.valueWidget ??
Text(
f.value ?? '',
style: TextStyle(fontSize: 13, color: context.tokens.text),
),
),
],
),
);
}
}