mta-mcp 1.0.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/README.md +818 -0
- package/agents/_TEMPLATE.md +153 -0
- package/agents/flutter.agent.md +222 -0
- package/agents/i18n.agent.md +78 -0
- package/agents/logicflow.agent.md +97 -0
- package/agents/vue3.agent.md +176 -0
- package/agents/wechat-miniprogram.agent.md +89 -0
- package/bin/mta.cjs +132 -0
- package/common/i18n.md +385 -0
- package/common/typescript-strict.md +186 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +6493 -0
- package/dist/index.js.map +1 -0
- package/package.json +81 -0
- package/standards/README.md +194 -0
- package/standards/core/code-generation.md +421 -0
- package/standards/core/code-style.md +308 -0
- package/standards/core/dart-base.md +572 -0
- package/standards/core/mandatory-rules.md +103 -0
- package/standards/core/typescript-base.md +179 -0
- package/standards/frameworks/flutter-ui-system.md +497 -0
- package/standards/frameworks/flutter.md +1268 -0
- package/standards/frameworks/pinia.md +172 -0
- package/standards/frameworks/vue3-composition.md +779 -0
- package/standards/frameworks/wechat-miniprogram.md +2177 -0
- package/standards/libraries/element-plus.md +1128 -0
- package/standards/libraries/i18n.md +360 -0
- package/standards/libraries/logicflow.md +1007 -0
- package/standards/patterns/api-layer.md +187 -0
- package/standards/patterns/component-design.md +200 -0
- package/standards/patterns/design-system-restoration.md +570 -0
- package/standards/patterns/vue-api-mock-layer.md +958 -0
- package/standards/patterns/vue-css-nesting.md +604 -0
- package/standards/troubleshooting-cases/flutter/textfield-vertical-centering.md +107 -0
- package/standards/workflows/design-restoration-guide.md +164 -0
- package/standards/workflows/large-project-split.md +359 -0
- package/standards/workflows/problem-diagnosis.md +280 -0
- package/standards/workflows/textfield-centering-guide.md +157 -0
- package/templates/README.md +144 -0
- package/templates/common/types/_CONFIG.md +12 -0
- package/templates/common/types/api.ts +39 -0
- package/templates/common/types/common.ts +70 -0
- package/templates/config-templates/agents-section.md +9 -0
- package/templates/config-templates/custom-section.md +6 -0
- package/templates/config-templates/header.md +29 -0
- package/templates/config-templates/workflow-minimal.md +44 -0
- package/templates/copilot-instructions-mcp-optimized.md +158 -0
- package/templates/vue/api-layer/_CONFIG.md +145 -0
- package/templates/vue/api-layer/index.ts +58 -0
- package/templates/vue/api-layer/mock/index.ts +122 -0
- package/templates/vue/api-layer/modules/_template.ts +109 -0
- package/templates/vue/api-layer/modules/index.ts +16 -0
- package/templates/vue/api-layer/request.ts +279 -0
- package/templates/vue/api-layer/types.ts +80 -0
- package/troubleshooting/README.md +368 -0
- package/troubleshooting/USAGE_GUIDE.md +289 -0
- package/troubleshooting/flutter/clip-/351/230/264/345/275/261/350/243/201/345/211/252.md +244 -0
- package/troubleshooting/flutter/component-/351/200/232/347/224/250/345/214/226/346/217/220/345/217/226.md +269 -0
- package/troubleshooting/flutter/input-/345/255/227/346/256/265/347/274/272/345/244/261.md +240 -0
- package/troubleshooting/flutter/input-/350/276/271/346/241/206/351/227/256/351/242/230.md +236 -0
- package/troubleshooting/flutter/layout-/345/260/272/345/257/270/344/270/215/345/214/271/351/205/215.md +214 -0
- package/troubleshooting/flutter/shadow-/351/200/217/345/207/272/351/227/256/351/242/230.md +172 -0
- package/troubleshooting/flutter/sketch-/345/210/227/350/241/250item/345/214/272/345/237/237.md +212 -0
- package/troubleshooting/flutter/sketch-/345/233/276/346/240/207/345/260/272/345/257/270.md +135 -0
- package/troubleshooting/flutter/sketch-/345/256/214/346/225/264/346/217/220/345/217/226.md +201 -0
- package/troubleshooting/flutter/sketch-/345/261/236/346/200/247/346/234/252/344/275/277/347/224/250.md +139 -0
- package/troubleshooting/flutter/sketch-/350/203/214/346/231/257/345/261/202/351/253/230/345/272/246.md +264 -0
- package/troubleshooting/flutter/svg-/346/234/252/345/261/205/344/270/255.md +120 -0
- package/troubleshooting/flutter/svg-/351/242/234/350/211/262/345/274/202/345/270/270.md +117 -0
- package/troubleshooting/flutter/tabbar-/345/212/250/347/224/273/345/220/214/346/255/245.md +107 -0
- package/troubleshooting/flutter/withopacity-/345/274/203/347/224/250.md +81 -0
- package/troubleshooting/vue3/cascader-/350/257/257/346/233/277/346/215/242.md +130 -0
- package/troubleshooting/vue3/drawer-input-/346/240/267/345/274/217.md +181 -0
- package/troubleshooting/vue3/table-/347/274/226/350/276/221/345/217/226/346/266/210.md +148 -0
- package/troubleshooting/vue3/table-/350/276/271/346/241/206/351/227/256/351/242/230.md +178 -0
package/troubleshooting/flutter/sketch-/345/210/227/350/241/250item/345/214/272/345/237/237.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Sketch 列表 Item 区域高度不一致问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `sketch`, `list`, `menu`, `item`, `padding`, `divider`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 中等(导致列表布局细节偏差)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "第一个/最后一个 item 高度不对"
|
|
13
|
+
- "列表 padding 不均匀"
|
|
14
|
+
- "divider 位置偏移"
|
|
15
|
+
- "底部/顶部留白太多/太少"
|
|
16
|
+
|
|
17
|
+
### 问题特征
|
|
18
|
+
- [ ] 列表首尾 item 与中间 item 高度不同
|
|
19
|
+
- [ ] 使用统一 padding 导致视觉不平衡
|
|
20
|
+
- [ ] 忽略 divider 在高度计算中的影响
|
|
21
|
+
- [ ] 假设所有 item 高度相同
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🎯 核心原理
|
|
26
|
+
|
|
27
|
+
**根本原因**:设计稿中列表的首个和末尾 item 通常有不同的 padding,以平衡与容器边缘的视觉关系。
|
|
28
|
+
|
|
29
|
+
### 典型 Sketch 测量结果
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Menu Card_background: 350px
|
|
33
|
+
|
|
34
|
+
Divider 位置:
|
|
35
|
+
- Divider 1: y=63
|
|
36
|
+
- Divider 2: y=119
|
|
37
|
+
- Divider 3: y=175
|
|
38
|
+
- Divider 4: y=231
|
|
39
|
+
- Divider 5: y=287
|
|
40
|
+
|
|
41
|
+
区域高度计算:
|
|
42
|
+
- Item 1: 0 → 63 = 63px
|
|
43
|
+
- Item 2: 63 → 119 = 56px
|
|
44
|
+
- Item 3: 119 → 175 = 56px
|
|
45
|
+
- Item 4: 175 → 231 = 56px
|
|
46
|
+
- Item 5: 231 → 287 = 56px
|
|
47
|
+
- Item 6: 287 → 350 = 63px
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**结论**: 首尾 item 63px,中间 item 56px,**不是**统一高度!
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## ❌ 常见错误
|
|
55
|
+
|
|
56
|
+
```dart
|
|
57
|
+
// ❌ 错误: 统一 padding
|
|
58
|
+
Padding(
|
|
59
|
+
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
|
|
60
|
+
child: MenuItem(),
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## ✅ 正确解决方案
|
|
67
|
+
|
|
68
|
+
### 1. 测量 Icon 精确位置
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
// 测量所有 icon 的 y 坐标
|
|
72
|
+
const icons = sketch.find('[name*="Icon BG"]', menuCard);
|
|
73
|
+
icons.forEach(icon => {
|
|
74
|
+
if (icon.frame.height === 36) {
|
|
75
|
+
console.log(`${icon.name}: y=${icon.frame.y}`);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// 结果:
|
|
80
|
+
// Item 1 Icon: y=20
|
|
81
|
+
// Item 2 Icon: y=76
|
|
82
|
+
// Item 3 Icon: y=132
|
|
83
|
+
// ...
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 2. 计算每个 item 的 padding
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Item 1 (区域 0→63):
|
|
90
|
+
- region_start = 0
|
|
91
|
+
- icon_y = 20 → top = 20
|
|
92
|
+
- icon_bottom = 56
|
|
93
|
+
- divider_y = 63 → bottom = 63 - 56 - 1 = 6
|
|
94
|
+
- 验证: 20 + 36 + 6 + 1 = 63 ✅
|
|
95
|
+
|
|
96
|
+
Item 2 (区域 64→119):
|
|
97
|
+
- region_start = 64 (divider1 + 1)
|
|
98
|
+
- icon_y = 76 → top = 76 - 64 = 12
|
|
99
|
+
- icon_bottom = 112
|
|
100
|
+
- divider_y = 119 → bottom = 119 - 112 - 1 = 6
|
|
101
|
+
- 验证: 12 + 36 + 6 + 1 = 55...
|
|
102
|
+
|
|
103
|
+
等等,56 - 1(divider) = 55,但 12+36+6=54?
|
|
104
|
+
让我重新计算:区域 = 56px 含 divider
|
|
105
|
+
内容区 = 55px,12 + 36 + 7 = 55 ✅
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 3. Flutter 精确实现
|
|
109
|
+
|
|
110
|
+
```dart
|
|
111
|
+
Widget _buildMenuItem(int index, MenuItem item) {
|
|
112
|
+
// Sketch 测量 (背景层 350px):
|
|
113
|
+
// - Item 1: 区域 63px = top:20 + icon:36 + bottom:6 + divider:1
|
|
114
|
+
// - Item 2-5: 区域 56px = top:12 + icon:36 + bottom:7 + divider:1
|
|
115
|
+
// - Item 6: 区域 63px = top:12 + icon:36 + bottom:15 (无 divider)
|
|
116
|
+
|
|
117
|
+
final isFirst = index == 0;
|
|
118
|
+
final isLast = index == 5; // 6 个 item
|
|
119
|
+
|
|
120
|
+
return Column(
|
|
121
|
+
mainAxisSize: MainAxisSize.min,
|
|
122
|
+
children: [
|
|
123
|
+
Padding(
|
|
124
|
+
padding: EdgeInsets.fromLTRB(
|
|
125
|
+
16,
|
|
126
|
+
isFirst ? 20 : 12, // 首 item top 更大
|
|
127
|
+
16,
|
|
128
|
+
isFirst ? 6 : (isLast ? 15 : 7), // 尾 item bottom 更大
|
|
129
|
+
),
|
|
130
|
+
child: Row(
|
|
131
|
+
children: [
|
|
132
|
+
Icon(size: 36),
|
|
133
|
+
SizedBox(width: 16),
|
|
134
|
+
Expanded(child: Text(item.title)),
|
|
135
|
+
ArrowIcon(),
|
|
136
|
+
],
|
|
137
|
+
),
|
|
138
|
+
),
|
|
139
|
+
|
|
140
|
+
// Divider (最后一个不需要)
|
|
141
|
+
if (!isLast)
|
|
142
|
+
Container(
|
|
143
|
+
margin: EdgeInsets.only(left: 68, right: 20),
|
|
144
|
+
height: 1,
|
|
145
|
+
color: Color(0x0F1C2B45),
|
|
146
|
+
),
|
|
147
|
+
],
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 🔑 关键公式
|
|
155
|
+
|
|
156
|
+
### 从 Sketch 测量计算 padding
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
top_padding = icon.y - region_start
|
|
160
|
+
bottom_padding = region_end - icon.bottom - divider_height
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
其中:
|
|
164
|
+
- `region_start` = 上一个 divider.y + 1 (首 item 为 0)
|
|
165
|
+
- `region_end` = 当前 divider.y (尾 item 为 background.height)
|
|
166
|
+
- `icon.bottom` = icon.y + icon.height
|
|
167
|
+
- `divider_height` = 1 (尾 item 为 0)
|
|
168
|
+
|
|
169
|
+
### 验证公式
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
区域高度 = top_padding + icon_height + bottom_padding + divider_height
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 📊 真实案例数据
|
|
178
|
+
|
|
179
|
+
### Profile Menu Card (350px)
|
|
180
|
+
|
|
181
|
+
| Item | 区域 | Top | Icon | Bottom | Divider | 总计 |
|
|
182
|
+
|------|------|-----|------|--------|---------|------|
|
|
183
|
+
| 1 | 0→63 | 20 | 36 | 6 | 1 | 63 |
|
|
184
|
+
| 2 | 63→119 | 12 | 36 | 7 | 1 | 56 |
|
|
185
|
+
| 3 | 119→175 | 12 | 36 | 7 | 1 | 56 |
|
|
186
|
+
| 4 | 175→231 | 12 | 36 | 7 | 1 | 56 |
|
|
187
|
+
| 5 | 231→287 | 12 | 36 | 7 | 1 | 56 |
|
|
188
|
+
| 6 | 287→350 | 12 | 36 | 15 | 0 | 63 |
|
|
189
|
+
|
|
190
|
+
**总计**: 63 + 56×4 + 63 = 350px ✅
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## ⚠️ 预防措施
|
|
195
|
+
|
|
196
|
+
1. **不要假设统一高度**: 始终测量首、中、尾三种 item
|
|
197
|
+
2. **测量 icon 实际 y 坐标**: 不要只看区域高度
|
|
198
|
+
3. **区分有无 divider**: 尾 item 通常无 divider
|
|
199
|
+
4. **使用背景层边界**: 不要用 Frame 高度
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 🔗 相关问题
|
|
204
|
+
|
|
205
|
+
- [sketch-背景层高度.md](./sketch-背景层高度.md) - 背景层 vs Frame 高度
|
|
206
|
+
- [layout-尺寸不匹配.md](./layout-尺寸不匹配.md) - 通用布局问题
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
**创建日期**: 2026-01-19
|
|
211
|
+
**最后更新**: 2026-01-19
|
|
212
|
+
**来源**: Profile 页面 Menu Item 布局调试实战
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Sketch 图标尺寸提取问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `sketch`, `icon`, `size`, `group`, `shape`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 中等(还原精度)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "图标尺寸与设计稿不符"
|
|
13
|
+
- "设置了尺寸但没效果"
|
|
14
|
+
- "Group 和 Shape 尺寸不一样"
|
|
15
|
+
- "提取的尺寸是外层容器的"
|
|
16
|
+
|
|
17
|
+
### 问题特征
|
|
18
|
+
- [ ] 从 Sketch 提取了图标尺寸
|
|
19
|
+
- [ ] 实际显示比预期大或小
|
|
20
|
+
- [ ] Sketch 中图标有 Group 包裹
|
|
21
|
+
- [ ] Group 尺寸与内部 Shape 尺寸不同
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🎯 核心原理
|
|
26
|
+
|
|
27
|
+
**根本原因**:Sketch 中图标通常有两层结构,外层 Group 用于布局定位,内层 Shape 才是实际视觉尺寸。
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Sketch 图标结构:
|
|
31
|
+
├─ Group "Tab Icon" (22x22) ← 外层容器,用于布局
|
|
32
|
+
│ └─ Shape "Icon_path" (14.67x14.67) ← 内部实际图形
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
如果提取外层 Group 的尺寸,图标会比设计稿看起来大。
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## ✅ 正确解决方案
|
|
40
|
+
|
|
41
|
+
### 使用脚本提取内部 Shape 尺寸
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
// Sketch 脚本:提取图标的内部实际尺寸
|
|
45
|
+
const sketch = require('sketch');
|
|
46
|
+
const document = sketch.getSelectedDocument();
|
|
47
|
+
const page = document.selectedPage;
|
|
48
|
+
|
|
49
|
+
function extractIconInternalSize(frameName) {
|
|
50
|
+
const frame = sketch.find(`[name="${frameName}"]`, page)[0];
|
|
51
|
+
if (!frame) return console.log('Frame not found');
|
|
52
|
+
|
|
53
|
+
function traverse(layer, parentY = 0) {
|
|
54
|
+
const f = layer.frame;
|
|
55
|
+
const absY = parentY + f.y;
|
|
56
|
+
|
|
57
|
+
if (layer.type === 'Group' && layer.name.includes('Icon')) {
|
|
58
|
+
console.log(`\n📦 ${layer.name} (外层 Group): ${f.width}x${f.height}`);
|
|
59
|
+
layer.layers?.forEach(child => {
|
|
60
|
+
if (child.type === 'Shape' || child.type === 'ShapePath') {
|
|
61
|
+
const cf = child.frame;
|
|
62
|
+
console.log(` └─ ${child.name} (内部图形): ${cf.width.toFixed(1)}x${cf.height.toFixed(1)} ← 使用这个`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (layer.layers) layer.layers.forEach(child => traverse(child, absY));
|
|
67
|
+
}
|
|
68
|
+
traverse(frame);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
extractIconInternalSize('Frame Name');
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 输出示例
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
📦 Tab Recipient Icon (外层 Group): 22x22
|
|
78
|
+
└─ Tab Recipient Icon_path_0 (内部图形): 14.7x14.7 ← 使用这个尺寸
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Flutter 中使用正确尺寸
|
|
82
|
+
|
|
83
|
+
```dart
|
|
84
|
+
// ✅ 使用内部图形的实际尺寸
|
|
85
|
+
SvgPicture.asset(
|
|
86
|
+
'assets/icons/recipient.svg',
|
|
87
|
+
width: 15.0, // 14.7 取整
|
|
88
|
+
height: 15.0,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
// ❌ 错误:使用外层 Group 尺寸
|
|
92
|
+
SvgPicture.asset(
|
|
93
|
+
'assets/icons/recipient.svg',
|
|
94
|
+
width: 22.0, // 这是 Group 尺寸,图标会偏大
|
|
95
|
+
height: 22.0,
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 📝 验证检查清单
|
|
102
|
+
|
|
103
|
+
1. **确认提取的是 Shape 尺寸**
|
|
104
|
+
- 不是 Group、Frame、Artboard 的尺寸
|
|
105
|
+
- 查看脚本输出中带 ← 标记的行
|
|
106
|
+
|
|
107
|
+
2. **视觉对比验证**
|
|
108
|
+
- 在模拟器/设备上与设计稿截图对比
|
|
109
|
+
- 图标边缘应与设计稿完全重合
|
|
110
|
+
|
|
111
|
+
3. **属性使用验证**
|
|
112
|
+
```bash
|
|
113
|
+
# 搜索确认尺寸属性被使用
|
|
114
|
+
grep -n "iconSize" lib/widgets/my_widget.dart
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
120
|
+
|
|
121
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
122
|
+
|----------|-----------|---------|
|
|
123
|
+
| 调整 Flutter 中的尺寸 | 如果基准就错了,调来调去 | 3-5 轮对话 |
|
|
124
|
+
| 添加 transform scale | Hack 方案,难以精确 | 2-3 轮对话 |
|
|
125
|
+
| 重新导出 SVG | 如果不知道问题在哪,重复错误 | 2-3 轮对话 |
|
|
126
|
+
|
|
127
|
+
**最佳做法**:先用脚本确认正确尺寸,再修改代码
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 📁 适用场景
|
|
132
|
+
|
|
133
|
+
- ✅ TabBar 图标
|
|
134
|
+
- ✅ 按钮内图标
|
|
135
|
+
- ✅ 任何从 Sketch 导出的图标
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Sketch 设计稿样式不完整提取问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `sketch`, `design`, `extraction`, `incomplete`, `一次性提取`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 高(导致多轮对话和返工)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "漏了xxx属性"
|
|
13
|
+
- "渐变怎么还原"
|
|
14
|
+
- "阴影参数是多少"
|
|
15
|
+
- "这个图标是圆形的吗"
|
|
16
|
+
- "设计稿上的圆角多大"
|
|
17
|
+
|
|
18
|
+
### 问题特征
|
|
19
|
+
- [ ] AI 每次只查询单个属性(颜色、尺寸、阴影等)
|
|
20
|
+
- [ ] 需要多轮对话才能获取完整样式信息
|
|
21
|
+
- [ ] 使用近似值代替精确值(如 Material Icons 代替 SVG)
|
|
22
|
+
- [ ] 假设属性而非验证(假设是圆形、假设颜色相同等)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🎯 核心原理
|
|
27
|
+
|
|
28
|
+
**根本原因**:分散查询导致信息不完整,浪费时间。
|
|
29
|
+
|
|
30
|
+
| 问题 | 表现 | 根因 |
|
|
31
|
+
|------|------|------|
|
|
32
|
+
| 属性读取不完整 | 漏读渐变、圆角、阴影参数 | 只读取部分属性 |
|
|
33
|
+
| 假设而非验证 | 假设圆形/颜色/图标 | 未从设计稿验证 |
|
|
34
|
+
| 使用近似值 | 用 Material Icons 代替 | 未导出原始 SVG |
|
|
35
|
+
| 分散查询 | 多轮对话才获取完整信息 | 每次只查一个属性 |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## ✅ 正确解决方案
|
|
40
|
+
|
|
41
|
+
### 强制执行:一次性完整提取脚本
|
|
42
|
+
|
|
43
|
+
**在还原任何 UI 元素前,必须使用以下脚本一次性提取所有属性:**
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// Sketch 完整样式提取脚本 - 必须使用
|
|
47
|
+
const sketch = require('sketch');
|
|
48
|
+
const document = sketch.getSelectedDocument();
|
|
49
|
+
const page = document.selectedPage;
|
|
50
|
+
|
|
51
|
+
function extractFullStyle(layerName) {
|
|
52
|
+
const layer = sketch.find(`[name="${layerName}"]`, page)[0];
|
|
53
|
+
if (!layer) return console.log(`Layer "${layerName}" not found`);
|
|
54
|
+
|
|
55
|
+
console.log('=== 基本信息 ===');
|
|
56
|
+
console.log(`Name: ${layer.name} (${layer.type})`);
|
|
57
|
+
console.log(`Frame: ${layer.frame.width}x${layer.frame.height} @ (${layer.frame.x}, ${layer.frame.y})`);
|
|
58
|
+
|
|
59
|
+
const style = layer.style;
|
|
60
|
+
|
|
61
|
+
// 1. 填充(颜色/渐变)
|
|
62
|
+
console.log('=== 填充 ===');
|
|
63
|
+
if (style.fills?.length) {
|
|
64
|
+
style.fills.forEach((fill, i) => {
|
|
65
|
+
console.log(`Fill ${i}: Type=${fill.fillType}, Enabled=${fill.enabled}`);
|
|
66
|
+
if (fill.fillType === 'Color') {
|
|
67
|
+
console.log(` Color: ${fill.color}`);
|
|
68
|
+
} else if (fill.fillType === 'Gradient') {
|
|
69
|
+
console.log(` Gradient: ${fill.gradient.gradientType}`);
|
|
70
|
+
console.log(` From: (${fill.gradient.from.x.toFixed(2)}, ${fill.gradient.from.y.toFixed(2)})`);
|
|
71
|
+
console.log(` To: (${fill.gradient.to.x.toFixed(2)}, ${fill.gradient.to.y.toFixed(2)})`);
|
|
72
|
+
fill.gradient.stops.forEach((stop, j) => {
|
|
73
|
+
console.log(` Stop ${j}: ${stop.color} @ ${stop.position}`);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
console.log('No fills');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 2. 边框
|
|
82
|
+
console.log('=== 边框 ===');
|
|
83
|
+
if (style.borders?.length) {
|
|
84
|
+
style.borders.forEach((border, i) => {
|
|
85
|
+
console.log(`Border ${i}: Color=${border.color}, Width=${border.thickness}, Position=${border.position}, Enabled=${border.enabled}`);
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
console.log('No borders');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 3. 阴影
|
|
92
|
+
console.log('=== 阴影 ===');
|
|
93
|
+
if (style.shadows?.length) {
|
|
94
|
+
style.shadows.forEach((s, i) => {
|
|
95
|
+
console.log(`Shadow ${i}: Color=${s.color}, Offset=(${s.x}, ${s.y}), Blur=${s.blur}, Spread=${s.spread}`);
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
console.log('No shadows');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 4. 内阴影
|
|
102
|
+
console.log('=== 内阴影 ===');
|
|
103
|
+
if (style.innerShadows?.length) {
|
|
104
|
+
style.innerShadows.forEach((s, i) => {
|
|
105
|
+
console.log(`InnerShadow ${i}: Color=${s.color}, Offset=(${s.x}, ${s.y}), Blur=${s.blur}, Spread=${s.spread}`);
|
|
106
|
+
});
|
|
107
|
+
} else {
|
|
108
|
+
console.log('No inner shadows');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 5. 圆角
|
|
112
|
+
console.log('=== 圆角 ===');
|
|
113
|
+
if (layer.layers?.[0]?.points) {
|
|
114
|
+
const points = layer.layers[0].points;
|
|
115
|
+
const radii = points.map(p => p.cornerRadius);
|
|
116
|
+
const allSame = radii.every(r => r === radii[0]);
|
|
117
|
+
if (allSame) {
|
|
118
|
+
console.log(`CornerRadius: ${radii[0]} (all corners)`);
|
|
119
|
+
} else {
|
|
120
|
+
console.log(`CornerRadius: [${radii.join(', ')}] (TL, TR, BR, BL)`);
|
|
121
|
+
}
|
|
122
|
+
} else if (layer.points) {
|
|
123
|
+
const radii = layer.points.map(p => p.cornerRadius);
|
|
124
|
+
console.log(`CornerRadius: ${radii[0]}`);
|
|
125
|
+
} else {
|
|
126
|
+
console.log('No corner radius (circle or custom shape)');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 6. 不透明度
|
|
130
|
+
console.log('=== 不透明度 ===');
|
|
131
|
+
console.log(`Opacity: ${style.opacity}`);
|
|
132
|
+
|
|
133
|
+
// 7. 子元素(如果是 Group)
|
|
134
|
+
if (layer.layers?.length) {
|
|
135
|
+
console.log('=== 子元素 ===');
|
|
136
|
+
layer.layers.forEach(child => {
|
|
137
|
+
console.log(`- ${child.name} (${child.type}): ${child.frame.width}x${child.frame.height}`);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 使用示例
|
|
143
|
+
extractFullStyle('Button Background');
|
|
144
|
+
extractFullStyle('Icon');
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 图标导出:必须使用 SVG
|
|
148
|
+
|
|
149
|
+
**禁止使用 Material Icons 或其他近似图标:**
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// 导出图标 SVG
|
|
153
|
+
const sketch = require('sketch');
|
|
154
|
+
const layer = sketch.find('[name="Icon Name"]', sketch.getSelectedDocument().selectedPage)[0];
|
|
155
|
+
if (layer) {
|
|
156
|
+
const svg = sketch.export(layer, { formats: 'svg', output: false });
|
|
157
|
+
console.log(svg.toString());
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 还原检查清单(强制)
|
|
162
|
+
|
|
163
|
+
在还原任何 UI 元素前,必须确认以下所有属性:
|
|
164
|
+
|
|
165
|
+
| 属性 | 检查项 | Flutter 对应 |
|
|
166
|
+
|------|--------|--------------|
|
|
167
|
+
| 尺寸 | width, height | `SizedBox(width:, height:)` |
|
|
168
|
+
| 位置 | x, y | `Positioned(left:, top:)` |
|
|
169
|
+
| 颜色/渐变 | fills[0] | `BoxDecoration(color:, gradient:)` |
|
|
170
|
+
| 圆角 | cornerRadius | `BorderRadius.circular()` |
|
|
171
|
+
| 边框 | borders | `Border.all()` |
|
|
172
|
+
| 阴影 | shadows | `BoxShadow()` |
|
|
173
|
+
| 透明度 | opacity | `Opacity(opacity:)` |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
178
|
+
|
|
179
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
180
|
+
|----------|-----------|---------|
|
|
181
|
+
| 每次只查一个属性 | 需要多轮对话才能获取完整信息 | 5-10 轮 |
|
|
182
|
+
| 假设是圆形 | 未验证 cornerRadius,可能是圆角矩形 | 2-3 轮 |
|
|
183
|
+
| 使用 Material Icons | 与设计稿不完全一致 | 3-5 轮 |
|
|
184
|
+
| 忽略渐变参数 | 只看到 from/to,未读取 stops | 2-4 轮 |
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 📁 适用场景
|
|
189
|
+
|
|
190
|
+
- ✅ 从 Sketch 设计稿还原任何 UI 元素
|
|
191
|
+
- ✅ 需要获取完整样式信息
|
|
192
|
+
- ✅ 包含渐变、阴影、复杂圆角的设计
|
|
193
|
+
- ✅ 图标还原(必须导出 SVG)
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 🔗 相关案例
|
|
198
|
+
|
|
199
|
+
- [sketch-图标尺寸.md](./sketch-图标尺寸.md) - 图标尺寸提取错误
|
|
200
|
+
- [svg-颜色异常.md](./svg-颜色异常.md) - SVG 颜色处理问题
|
|
201
|
+
- [svg-未居中.md](./svg-未居中.md) - SVG 居中问题
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# 组件属性定义但未使用问题
|
|
2
|
+
|
|
3
|
+
> **问题标签**: `property`, `unused`, `debug`, `widget`
|
|
4
|
+
> **框架**: Flutter
|
|
5
|
+
> **严重程度**: 高(功能无效)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔍 问题识别
|
|
10
|
+
|
|
11
|
+
### 用户描述关键词
|
|
12
|
+
- "设置了属性但没效果"
|
|
13
|
+
- "参数不生效"
|
|
14
|
+
- "改了值但 UI 没变化"
|
|
15
|
+
- "属性没有被使用"
|
|
16
|
+
|
|
17
|
+
### 问题特征
|
|
18
|
+
- [ ] 在组件类中定义了新属性
|
|
19
|
+
- [ ] 修改属性值后 UI 没有变化
|
|
20
|
+
- [ ] 没有编译错误
|
|
21
|
+
- [ ] 运行正常但功能不符合预期
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🎯 核心原理
|
|
26
|
+
|
|
27
|
+
**根本原因**:在类中定义了属性,但在 `build` 方法中没有实际使用该属性。
|
|
28
|
+
|
|
29
|
+
```dart
|
|
30
|
+
// ❌ 错误示例:定义了 iconSize 但没使用
|
|
31
|
+
class MyIcon extends StatelessWidget {
|
|
32
|
+
const MyIcon({
|
|
33
|
+
super.key,
|
|
34
|
+
this.iconSize = 24.0, // 定义了属性
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
final double iconSize; // 有这个字段
|
|
38
|
+
|
|
39
|
+
@override
|
|
40
|
+
Widget build(BuildContext context) {
|
|
41
|
+
return Icon(
|
|
42
|
+
Icons.star,
|
|
43
|
+
size: 24.0, // ← 直接写死了!没用 iconSize
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## ✅ 正确解决方案
|
|
52
|
+
|
|
53
|
+
### 1. 验证属性被使用
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 搜索属性在文件中的使用情况
|
|
57
|
+
grep -n "iconSize" lib/widgets/my_icon.dart
|
|
58
|
+
|
|
59
|
+
# 期望输出:
|
|
60
|
+
# 5: this.iconSize = 24.0, ← 定义
|
|
61
|
+
# 8: final double iconSize; ← 字段
|
|
62
|
+
# 14: size: iconSize, ← 使用 ✅
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. 正确使用属性
|
|
66
|
+
|
|
67
|
+
```dart
|
|
68
|
+
// ✅ 正确示例
|
|
69
|
+
class MyIcon extends StatelessWidget {
|
|
70
|
+
const MyIcon({
|
|
71
|
+
super.key,
|
|
72
|
+
this.iconSize = 24.0,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
final double iconSize;
|
|
76
|
+
|
|
77
|
+
@override
|
|
78
|
+
Widget build(BuildContext context) {
|
|
79
|
+
return Icon(
|
|
80
|
+
Icons.star,
|
|
81
|
+
size: iconSize, // ← 使用属性
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. 添加属性后的验证清单
|
|
88
|
+
|
|
89
|
+
每次添加新属性后必须检查:
|
|
90
|
+
|
|
91
|
+
- [ ] 属性在 constructor 中定义
|
|
92
|
+
- [ ] 属性有对应的 final 字段
|
|
93
|
+
- [ ] 属性在 build 方法中被引用
|
|
94
|
+
- [ ] 修改属性值后 UI 确实变化
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 📝 快速调试方法
|
|
99
|
+
|
|
100
|
+
```dart
|
|
101
|
+
// 临时添加打印语句验证
|
|
102
|
+
@override
|
|
103
|
+
Widget build(BuildContext context) {
|
|
104
|
+
print('🔍 iconSize: $iconSize'); // 确认值正确
|
|
105
|
+
return Icon(
|
|
106
|
+
Icons.star,
|
|
107
|
+
size: iconSize,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```dart
|
|
113
|
+
// 或使用断言
|
|
114
|
+
@override
|
|
115
|
+
Widget build(BuildContext context) {
|
|
116
|
+
assert(iconSize > 0, 'iconSize must be positive');
|
|
117
|
+
// ...
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## ❌ 常见错误排查路线(避免重复)
|
|
124
|
+
|
|
125
|
+
| 尝试方向 | 为什么无效 | 浪费时间 |
|
|
126
|
+
|----------|-----------|---------|
|
|
127
|
+
| 调整属性默认值 | 如果没使用,改默认值也没用 | 1-2 轮对话 |
|
|
128
|
+
| 添加更多属性 | 同样的问题会重复 | 2-3 轮对话 |
|
|
129
|
+
| 检查父组件传值 | 如果子组件没用,传什么都一样 | 1-2 轮对话 |
|
|
130
|
+
|
|
131
|
+
**最佳做法**:先 grep 确认属性被使用,再调试其他问题
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 📁 适用场景
|
|
136
|
+
|
|
137
|
+
- ✅ 自定义 Widget 开发
|
|
138
|
+
- ✅ 组件可配置化
|
|
139
|
+
- ✅ 任何涉及属性传递的场景
|