mta-mcp 2.17.0 → 2.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mta-mcp",
3
- "version": "2.17.0",
3
+ "version": "2.18.0",
4
4
  "description": "MTA - 智能项目分析与编码规范管理 MCP 服务器",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,269 @@
1
+ # 通用组件提取与文本自适应对齐
2
+
3
+ > **问题标签**: `component`, `refactor`, `text-align`, `adaptive`, `center`, `left`
4
+ > **框架**: Flutter
5
+ > **严重程度**: 中等(影响代码可维护性和 UI 一致性)
6
+
7
+ ---
8
+
9
+ ## 🔍 问题识别
10
+
11
+ ### 用户描述关键词
12
+ - "这两个卡片样式很像"
13
+ - "单行居中、多行左对齐"
14
+ - "重复代码太多"
15
+ - "不同状态显示不同样式"
16
+
17
+ ### 问题特征
18
+ - [ ] 多个相似组件(如 Warning Card、Error Card)硬编码实现
19
+ - [ ] 相同的布局结构重复多次
20
+ - [ ] 文本对齐方式需要根据内容长度动态调整
21
+ - [ ] 颜色/图标等仅通过状态切换
22
+
23
+ ---
24
+
25
+ ## 🎯 核心原理
26
+
27
+ ### 问题 1:硬编码重复组件
28
+
29
+ **错误做法**:为每种状态创建独立的 Widget 方法
30
+
31
+ ```dart
32
+ // ❌ 硬编码 - 重复代码
33
+ Widget _buildWarningCard() { ... }
34
+ Widget _buildErrorCard() { ... }
35
+ Widget _buildSuccessCard() { ... }
36
+ ```
37
+
38
+ **正确做法**:提取通用组件,通过参数配置样式
39
+
40
+ ```dart
41
+ // ✅ 通用组件
42
+ TipCard(
43
+ type: TipCardType.warning, // 或 error, success, info
44
+ title: '标题',
45
+ content: '内容',
46
+ buttonText: '按钮',
47
+ onButtonPressed: () {},
48
+ )
49
+ ```
50
+
51
+ ### 问题 2:文本单行居中、多行左对齐
52
+
53
+ **需求**:
54
+ - 短文本(单行):水平居中显示
55
+ - 长文本(多行):左对齐显示
56
+
57
+ **解决方案**:使用 `LayoutBuilder` + `TextPainter` 动态计算
58
+
59
+ ```dart
60
+ Widget _buildAdaptiveText(String content, TextStyle style) {
61
+ return LayoutBuilder(
62
+ builder: (context, constraints) {
63
+ // 计算文本是否超出一行
64
+ final textPainter = TextPainter(
65
+ text: TextSpan(text: content, style: style),
66
+ maxLines: 1,
67
+ textDirection: TextDirection.ltr,
68
+ );
69
+ textPainter.layout(maxWidth: constraints.maxWidth);
70
+
71
+ // 判断是否溢出
72
+ final isMultiLine = textPainter.didExceedMaxLines ||
73
+ textPainter.width >= constraints.maxWidth * 0.95;
74
+
75
+ return Container(
76
+ width: double.infinity,
77
+ alignment: isMultiLine ? Alignment.centerLeft : Alignment.center,
78
+ child: Text(
79
+ content,
80
+ style: style,
81
+ textAlign: isMultiLine ? TextAlign.left : TextAlign.center,
82
+ maxLines: 2,
83
+ overflow: TextOverflow.ellipsis,
84
+ ),
85
+ );
86
+ },
87
+ );
88
+ }
89
+ ```
90
+
91
+ ---
92
+
93
+ ## ✅ 完整解决方案
94
+
95
+ ### 1. 定义配置类
96
+
97
+ ```dart
98
+ /// TipCard 类型枚举
99
+ enum TipCardType { warning, error, success, info }
100
+
101
+ /// 配置类 - 所有颜色来自 Sketch
102
+ class TipCardConfig {
103
+ final Color backgroundColor;
104
+ final Color borderColor;
105
+ final Color titleColor;
106
+ final Color contentColor;
107
+ final Color buttonShadowColor;
108
+ final String iconPath;
109
+
110
+ const TipCardConfig({...});
111
+
112
+ /// Warning: #fff8e7ff, border #ffe4b5ff
113
+ static const warning = TipCardConfig(
114
+ backgroundColor: Color(0xFFFFF8E7),
115
+ borderColor: Color(0xFFFFE4B5),
116
+ titleColor: Color(0xFFFF9800),
117
+ contentColor: Color(0xFFB45309),
118
+ buttonShadowColor: Color(0x40FF9800),
119
+ iconPath: 'assets/icons/warning_icon.svg',
120
+ );
121
+
122
+ /// Error: #f4433614 (8%透明), border #ffcacaff
123
+ static const error = TipCardConfig(
124
+ backgroundColor: Color(0x14F44336),
125
+ borderColor: Color(0xFFFFCACA),
126
+ titleColor: Color(0xFFF44336),
127
+ contentColor: Color(0xFFCA4F57),
128
+ buttonShadowColor: Color(0x40D0121B),
129
+ iconPath: 'assets/icons/error_icon.svg',
130
+ );
131
+
132
+ static TipCardConfig fromType(TipCardType type) {
133
+ switch (type) {
134
+ case TipCardType.warning: return warning;
135
+ case TipCardType.error: return error;
136
+ // ...
137
+ }
138
+ }
139
+ }
140
+ ```
141
+
142
+ ### 2. 创建通用组件
143
+
144
+ ```dart
145
+ class TipCard extends StatelessWidget {
146
+ final TipCardType type;
147
+ final String title;
148
+ final String content;
149
+ final String buttonText;
150
+ final VoidCallback? onButtonPressed;
151
+
152
+ const TipCard({
153
+ super.key,
154
+ required this.type,
155
+ required this.title,
156
+ required this.content,
157
+ required this.buttonText,
158
+ this.onButtonPressed,
159
+ });
160
+
161
+ @override
162
+ Widget build(BuildContext context) {
163
+ final config = TipCardConfig.fromType(type);
164
+
165
+ return Container(
166
+ padding: const EdgeInsets.fromLTRB(23, 17, 23, 10),
167
+ decoration: BoxDecoration(
168
+ color: config.backgroundColor,
169
+ borderRadius: BorderRadius.circular(16),
170
+ border: Border.all(color: config.borderColor, width: 1),
171
+ ),
172
+ child: Column(
173
+ children: [
174
+ _buildTitleRow(config),
175
+ const SizedBox(height: 8),
176
+ _buildAdaptiveContent(config), // 自适应对齐
177
+ const SizedBox(height: 10),
178
+ _buildButton(config),
179
+ ],
180
+ ),
181
+ );
182
+ }
183
+ }
184
+ ```
185
+
186
+ ### 3. 使用组件
187
+
188
+ ```dart
189
+ // 替换硬编码实现
190
+ Widget _buildWarningCard(ProfileController controller) {
191
+ return Obx(() {
192
+ final status = controller.kycStatus.value;
193
+
194
+ if (status == KycStatus.expiring) {
195
+ return TipCard(
196
+ type: TipCardType.warning,
197
+ title: '证件即将过期',
198
+ content: '您的身份证件将于30天后过期,请及时更新。',
199
+ buttonText: '立即更新',
200
+ onButtonPressed: controller.updateIdDocument,
201
+ );
202
+ } else if (status == KycStatus.rejected) {
203
+ return TipCard(
204
+ type: TipCardType.error,
205
+ title: '验证失败',
206
+ content: '证件照片不清晰,请重新上传。', // 短文本会居中
207
+ buttonText: '重新验证',
208
+ onButtonPressed: controller.resubmitVerification,
209
+ );
210
+ }
211
+
212
+ return const SizedBox.shrink();
213
+ });
214
+ }
215
+ ```
216
+
217
+ ---
218
+
219
+ ## 🔑 关键公式
220
+
221
+ ### TextPainter 溢出检测
222
+
223
+ ```dart
224
+ final isMultiLine = textPainter.didExceedMaxLines ||
225
+ textPainter.width >= constraints.maxWidth * 0.95;
226
+ ```
227
+
228
+ - `didExceedMaxLines`:文本是否超出 maxLines
229
+ - `width >= maxWidth * 0.95`:文本宽度接近容器宽度(留 5% 余量)
230
+
231
+ ### 配置类设计模式
232
+
233
+ ```dart
234
+ // 枚举 + 静态配置 + fromType 工厂方法
235
+ enum Type { a, b, c }
236
+
237
+ class Config {
238
+ static const a = Config(...);
239
+ static const b = Config(...);
240
+
241
+ static Config fromType(Type type) => switch (type) {
242
+ Type.a => a,
243
+ Type.b => b,
244
+ // ...
245
+ };
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ ## ⚠️ 预防措施
252
+
253
+ 1. **识别相似组件**:当发现 2+ 个组件结构相同、仅样式不同时,立即提取
254
+ 2. **颜色来自设计稿**:使用 Sketch MCP 工具获取精确颜色值
255
+ 3. **文本对齐需求确认**:明确单行/多行的对齐规则
256
+ 4. **配置集中管理**:将所有变体配置放在一个类中
257
+
258
+ ---
259
+
260
+ ## 🔗 相关问题
261
+
262
+ - [sketch-背景层高度.md](./sketch-背景层高度.md) - 获取精确尺寸
263
+ - [sketch-完整提取.md](./sketch-完整提取.md) - 一次性提取样式
264
+
265
+ ---
266
+
267
+ **创建日期**: 2026-01-20
268
+ **最后更新**: 2026-01-20
269
+ **来源**: Profile 页面 TipCard 组件重构实战