mta-mcp 2.17.0 → 3.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.
Files changed (38) hide show
  1. package/agents/flutter.agent.md +23 -64
  2. package/agents/mta.agent.md +226 -0
  3. package/agents/vue3.agent.md +24 -0
  4. package/agents/wechat-miniprogram.agent.md +23 -0
  5. package/dist/index.js +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -4
  8. package/standards/mcp-tools/sketch-mcp.md +497 -0
  9. package/standards/patterns/design-system-restoration.md +570 -0
  10. package/standards/syntax-mapping.md +256 -0
  11. package/standards/workflows/design-restoration.md +401 -0
  12. package/templates/README.md +14 -0
  13. package/templates/design-measurement/README.md +81 -0
  14. package/templates/design-measurement/component-measurement.js +52 -0
  15. package/templates/design-measurement/gap-measurement.js +79 -0
  16. package/templates/design-measurement/style-extraction.js +109 -0
  17. package/common/i18n.md +0 -385
  18. package/common/typescript-strict.md +0 -186
  19. package/troubleshooting/README.md +0 -368
  20. package/troubleshooting/USAGE_GUIDE.md +0 -289
  21. package/troubleshooting/flutter/clip-/351/230/264/345/275/261/350/243/201/345/211/252.md +0 -244
  22. package/troubleshooting/flutter/input-/345/255/227/346/256/265/347/274/272/345/244/261.md +0 -240
  23. package/troubleshooting/flutter/input-/350/276/271/346/241/206/351/227/256/351/242/230.md +0 -236
  24. package/troubleshooting/flutter/layout-/345/260/272/345/257/270/344/270/215/345/214/271/351/205/215.md +0 -214
  25. package/troubleshooting/flutter/shadow-/351/200/217/345/207/272/351/227/256/351/242/230.md +0 -172
  26. package/troubleshooting/flutter/sketch-/345/210/227/350/241/250item/345/214/272/345/237/237.md +0 -212
  27. package/troubleshooting/flutter/sketch-/345/233/276/346/240/207/345/260/272/345/257/270.md +0 -135
  28. package/troubleshooting/flutter/sketch-/345/256/214/346/225/264/346/217/220/345/217/226.md +0 -201
  29. package/troubleshooting/flutter/sketch-/345/261/236/346/200/247/346/234/252/344/275/277/347/224/250.md +0 -139
  30. package/troubleshooting/flutter/sketch-/350/203/214/346/231/257/345/261/202/351/253/230/345/272/246.md +0 -264
  31. package/troubleshooting/flutter/svg-/346/234/252/345/261/205/344/270/255.md +0 -120
  32. package/troubleshooting/flutter/svg-/351/242/234/350/211/262/345/274/202/345/270/270.md +0 -117
  33. package/troubleshooting/flutter/tabbar-/345/212/250/347/224/273/345/220/214/346/255/245.md +0 -107
  34. package/troubleshooting/flutter/withopacity-/345/274/203/347/224/250.md +0 -81
  35. package/troubleshooting/vue3/cascader-/350/257/257/346/233/277/346/215/242.md +0 -130
  36. package/troubleshooting/vue3/drawer-input-/346/240/267/345/274/217.md +0 -181
  37. package/troubleshooting/vue3/table-/347/274/226/350/276/221/345/217/226/346/266/210.md +0 -148
  38. package/troubleshooting/vue3/table-/350/276/271/346/241/206/351/227/256/351/242/230.md +0 -178
@@ -0,0 +1,81 @@
1
+ # 🎨 设计稿测量模板
2
+
3
+ > 适用于 Sketch MCP、Figma 等设计工具的测量脚本模板
4
+
5
+ ---
6
+
7
+ ## 🤖 Agent 使用指令
8
+
9
+ **触发词**:
10
+ - "测量选中的组件"
11
+ - "获取设计稿边距"
12
+ - "分析图层样式"
13
+ - "获取渐变色参数"
14
+
15
+ **自动匹配模板**:
16
+
17
+ | 场景 | 对应模板 |
18
+ |------|----------|
19
+ | 分析整体结构 | `structure-overview.js` |
20
+ | 测量单个组件 | `component-measurement.js` |
21
+ | 计算间距关系 | `gap-measurement.js` |
22
+ | 提取样式参数 | `style-extraction.js` |
23
+ | 分析嵌套Symbol | `nested-symbol-analysis.js` |
24
+
25
+ ---
26
+
27
+ ## 📁 模板文件
28
+
29
+ ### 1. structure-overview.js
30
+ 用途:分析选中元素的整体结构和层级
31
+
32
+ ### 2. component-measurement.js
33
+ 用途:测量单个组件的尺寸、边距、内边距(自动计算相对值)
34
+
35
+ ### 3. gap-measurement.js
36
+ 用途:计算同级元素之间的间距
37
+
38
+ ### 4. style-extraction.js
39
+ 用途:提取颜色、渐变、阴影、圆角等样式参数
40
+
41
+ ### 5. nested-symbol-analysis.js
42
+ 用途:递归分析 Symbol 内部嵌套结构
43
+
44
+ ---
45
+
46
+ ## 🔧 使用方式
47
+
48
+ ### 方式一:直接调用 MCP 工具
49
+ ```
50
+ 调用 get_standard_by_id({ id: 'sketch-mcp' }) 获取完整脚本
51
+ ```
52
+
53
+ ### 方式二:复制模板脚本
54
+ ```javascript
55
+ // 从模板文件中复制脚本到 mcp_sketch_run_code
56
+ ```
57
+
58
+ ---
59
+
60
+ ## ⚠️ 核心原则
61
+
62
+ 1. **测量 = 记录 + 计算**
63
+ - 记录绝对坐标
64
+ - 立即计算相对边距
65
+ - 转换为目标框架可用的值
66
+
67
+ 2. **坐标系转换**
68
+ - iOS SafeArea 偏移:44px(刘海屏)/ 20px(非刘海)
69
+ - Android 状态栏:24-48dp
70
+ - Web:通常不需要
71
+
72
+ 3. **渐变角度转换**
73
+ - Sketch: from{x,y} → to{x,y}(0-1 范围)
74
+ - CSS: `linear-gradient(angle, ...)`
75
+ - Flutter: `Alignment(x, y)`(-1 到 1 范围)
76
+
77
+ ---
78
+
79
+ **维护者**: MTA工作室
80
+ **版本**: v1.0
81
+ **创建日期**: 2025-01-21
@@ -0,0 +1,52 @@
1
+ /**
2
+ * 组件测量模板 - 自动计算相对边距
3
+ * 用途:测量单个组件的完整布局参数
4
+ *
5
+ * 使用方式:在 Sketch 中选中目标组件,运行此脚本
6
+ */
7
+ const sketch = require('sketch')
8
+
9
+ const sel = sketch.getSelectedDocument().selectedLayers.layers
10
+ if (!sel.length) {
11
+ console.log('请先选中一个组件')
12
+ } else {
13
+ const layer = sel[0]
14
+ const parent = layer.parent
15
+
16
+ // 基础信息
17
+ console.log('=== 组件测量结果 ===')
18
+ console.log(`名称: ${layer.name}`)
19
+ console.log(`类型: ${layer.type}`)
20
+
21
+ // 尺寸信息
22
+ console.log(`\n--- 尺寸 ---`)
23
+ console.log(`宽度: ${layer.frame.width}`)
24
+ console.log(`高度: ${layer.frame.height}`)
25
+
26
+ // 绝对坐标(供参考)
27
+ console.log(`\n--- 绝对坐标(供参考)---`)
28
+ console.log(`x: ${layer.frame.x}`)
29
+ console.log(`y: ${layer.frame.y}`)
30
+
31
+ // ⭐ 关键:计算相对边距(可直接使用)
32
+ console.log(`\n--- 相对边距(可直接使用)---`)
33
+ if (parent && parent.frame) {
34
+ const paddingLeft = layer.frame.x - parent.frame.x
35
+ const paddingTop = layer.frame.y - parent.frame.y
36
+ const paddingRight = (parent.frame.x + parent.frame.width) - (layer.frame.x + layer.frame.width)
37
+ const paddingBottom = (parent.frame.y + parent.frame.height) - (layer.frame.y + layer.frame.height)
38
+
39
+ console.log(`距父容器左边: ${paddingLeft}`)
40
+ console.log(`距父容器顶部: ${paddingTop}`)
41
+ console.log(`距父容器右边: ${paddingRight}`)
42
+ console.log(`距父容器底部: ${paddingBottom}`)
43
+ console.log(`父容器: ${parent.name} (${parent.frame.width}x${parent.frame.height})`)
44
+ } else {
45
+ console.log('无父容器或为顶层元素')
46
+ }
47
+
48
+ // 框架转换提示
49
+ console.log(`\n--- 框架转换 ---`)
50
+ console.log(`Flutter: padding: EdgeInsets.fromLTRB(${layer.frame.x}, ${layer.frame.y}, 0, 0)`)
51
+ console.log(`CSS: padding: ${layer.frame.y}px ${layer.frame.x}px;`)
52
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * 间距测量模板 - 计算同级元素间距
3
+ * 用途:测量多个元素之间的垂直/水平间距
4
+ *
5
+ * 使用方式:在 Sketch 中选中多个同级元素,运行此脚本
6
+ */
7
+ const sketch = require('sketch')
8
+
9
+ const sel = sketch.getSelectedDocument().selectedLayers.layers
10
+ if (sel.length < 2) {
11
+ console.log('请选中至少2个元素来计算间距')
12
+ } else {
13
+ // 按 Y 坐标排序(从上到下)
14
+ const sortedByY = [...sel].sort((a, b) => a.frame.y - b.frame.y)
15
+
16
+ console.log('=== 垂直间距测量 ===')
17
+ console.log(`选中元素数量: ${sel.length}`)
18
+
19
+ for (let i = 0; i < sortedByY.length - 1; i++) {
20
+ const current = sortedByY[i]
21
+ const next = sortedByY[i + 1]
22
+
23
+ // 计算垂直间距 = 下一个元素顶部 - 当前元素底部
24
+ const gap = next.frame.y - (current.frame.y + current.frame.height)
25
+
26
+ console.log(`\n${current.name} ↓ ${next.name}`)
27
+ console.log(` 垂直间距: ${gap}px`)
28
+ console.log(` CSS: margin-bottom: ${gap}px; 或 gap: ${gap}px;`)
29
+ console.log(` Flutter: SizedBox(height: ${gap})`)
30
+ }
31
+
32
+ // 按 X 坐标排序(从左到右)
33
+ const sortedByX = [...sel].sort((a, b) => a.frame.x - b.frame.x)
34
+
35
+ console.log('\n=== 水平间距测量 ===')
36
+
37
+ for (let i = 0; i < sortedByX.length - 1; i++) {
38
+ const current = sortedByX[i]
39
+ const next = sortedByX[i + 1]
40
+
41
+ // 计算水平间距 = 下一个元素左边 - 当前元素右边
42
+ const gap = next.frame.x - (current.frame.x + current.frame.width)
43
+
44
+ console.log(`\n${current.name} → ${next.name}`)
45
+ console.log(` 水平间距: ${gap}px`)
46
+ console.log(` CSS: margin-right: ${gap}px; 或 gap: ${gap}px;`)
47
+ console.log(` Flutter: SizedBox(width: ${gap})`)
48
+ }
49
+
50
+ // 汇总信息
51
+ console.log('\n=== 汇总 ===')
52
+ const verticalGaps = []
53
+ const horizontalGaps = []
54
+
55
+ for (let i = 0; i < sortedByY.length - 1; i++) {
56
+ const gap = sortedByY[i + 1].frame.y - (sortedByY[i].frame.y + sortedByY[i].frame.height)
57
+ verticalGaps.push(gap)
58
+ }
59
+
60
+ for (let i = 0; i < sortedByX.length - 1; i++) {
61
+ const gap = sortedByX[i + 1].frame.x - (sortedByX[i].frame.x + sortedByX[i].frame.width)
62
+ horizontalGaps.push(gap)
63
+ }
64
+
65
+ const uniqueVGaps = [...new Set(verticalGaps)]
66
+ const uniqueHGaps = [...new Set(horizontalGaps)]
67
+
68
+ if (uniqueVGaps.length === 1) {
69
+ console.log(`垂直间距统一: ${uniqueVGaps[0]}px → 可使用 flex gap 或 Column mainAxisSpacing`)
70
+ } else {
71
+ console.log(`垂直间距不统一: ${verticalGaps.join(', ')} → 需要单独设置`)
72
+ }
73
+
74
+ if (uniqueHGaps.length === 1) {
75
+ console.log(`水平间距统一: ${uniqueHGaps[0]}px → 可使用 flex gap 或 Row mainAxisSpacing`)
76
+ } else {
77
+ console.log(`水平间距不统一: ${horizontalGaps.join(', ')} → 需要单独设置`)
78
+ }
79
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * 样式提取模板 - 完整样式参数
3
+ * 用途:提取颜色、渐变、阴影、圆角等样式
4
+ *
5
+ * 使用方式:在 Sketch 中选中目标元素,运行此脚本
6
+ */
7
+ const sketch = require('sketch')
8
+
9
+ const sel = sketch.getSelectedDocument().selectedLayers.layers
10
+ if (!sel.length) {
11
+ console.log('请先选中一个元素')
12
+ } else {
13
+ const layer = sel[0]
14
+ const style = layer.style
15
+
16
+ console.log('=== 样式提取结果 ===')
17
+ console.log(`元素: ${layer.name}`)
18
+
19
+ // 圆角
20
+ if (layer.style?.corners) {
21
+ console.log(`\n--- 圆角 ---`)
22
+ console.log(`圆角: ${JSON.stringify(layer.style.corners.radii)}`)
23
+ }
24
+
25
+ // 填充(颜色/渐变)
26
+ if (style?.fills?.length) {
27
+ console.log(`\n--- 填充 ---`)
28
+ style.fills.forEach((fill, i) => {
29
+ if (fill.fillType === 'Color') {
30
+ console.log(`填充${i}: 纯色 ${fill.color}`)
31
+ // 转换提示
32
+ const hex = fill.color
33
+ console.log(` CSS: background: ${hex};`)
34
+ console.log(` Flutter: Color(0xFF${hex.replace('#', '').toUpperCase()})`)
35
+ } else if (fill.fillType === 'Gradient') {
36
+ console.log(`填充${i}: 渐变`)
37
+ console.log(` 类型: ${fill.gradient.gradientType}`)
38
+ console.log(` 起点: from(${fill.gradient.from.x}, ${fill.gradient.from.y})`)
39
+ console.log(` 终点: to(${fill.gradient.to.x}, ${fill.gradient.to.y})`)
40
+ console.log(` 色标:`)
41
+ fill.gradient.stops.forEach((stop, j) => {
42
+ console.log(` ${j}: ${stop.color} at ${(stop.position * 100).toFixed(0)}%`)
43
+ })
44
+
45
+ // 计算渐变角度
46
+ const dx = fill.gradient.to.x - fill.gradient.from.x
47
+ const dy = fill.gradient.to.y - fill.gradient.from.y
48
+ const angle = Math.round(Math.atan2(dy, dx) * 180 / Math.PI + 90)
49
+
50
+ // CSS 转换
51
+ const colors = fill.gradient.stops.map(s => `${s.color} ${(s.position * 100).toFixed(0)}%`).join(', ')
52
+ console.log(` CSS: linear-gradient(${angle}deg, ${colors})`)
53
+
54
+ // Flutter 转换
55
+ const fromX = (fill.gradient.from.x * 2 - 1).toFixed(2)
56
+ const fromY = (fill.gradient.from.y * 2 - 1).toFixed(2)
57
+ const toX = (fill.gradient.to.x * 2 - 1).toFixed(2)
58
+ const toY = (fill.gradient.to.y * 2 - 1).toFixed(2)
59
+ console.log(` Flutter: begin: Alignment(${fromX}, ${fromY}), end: Alignment(${toX}, ${toY})`)
60
+ }
61
+ })
62
+ }
63
+
64
+ // 边框
65
+ if (style?.borders?.length) {
66
+ console.log(`\n--- 边框 ---`)
67
+ style.borders.forEach((border, i) => {
68
+ console.log(`边框${i}: ${border.color} 宽度=${border.thickness}`)
69
+ console.log(` CSS: border: ${border.thickness}px solid ${border.color};`)
70
+ })
71
+ }
72
+
73
+ // 阴影
74
+ if (style?.shadows?.length) {
75
+ console.log(`\n--- 阴影 ---`)
76
+ style.shadows.forEach((shadow, i) => {
77
+ console.log(`阴影${i}:`)
78
+ console.log(` 颜色: ${shadow.color}`)
79
+ console.log(` 偏移: x=${shadow.x}, y=${shadow.y}`)
80
+ console.log(` 模糊: ${shadow.blur}`)
81
+ console.log(` 扩展: ${shadow.spread}`)
82
+ console.log(` CSS: box-shadow: ${shadow.x}px ${shadow.y}px ${shadow.blur}px ${shadow.spread}px ${shadow.color};`)
83
+ })
84
+ }
85
+
86
+ // 内阴影
87
+ if (style?.innerShadows?.length) {
88
+ console.log(`\n--- 内阴影 ---`)
89
+ style.innerShadows.forEach((shadow, i) => {
90
+ console.log(`内阴影${i}:`)
91
+ console.log(` 颜色: ${shadow.color}`)
92
+ console.log(` 偏移: x=${shadow.x}, y=${shadow.y}`)
93
+ console.log(` 模糊: ${shadow.blur}`)
94
+ console.log(` CSS: box-shadow: inset ${shadow.x}px ${shadow.y}px ${shadow.blur}px ${shadow.color};`)
95
+ })
96
+ }
97
+
98
+ // 文字样式(如果是文本)
99
+ if (layer.type === 'Text') {
100
+ console.log(`\n--- 文字样式 ---`)
101
+ const textStyle = layer.style
102
+ console.log(`内容: ${layer.text}`)
103
+ console.log(`字体: ${textStyle.fontFamily || 'default'}`)
104
+ console.log(`字号: ${textStyle.fontSize}`)
105
+ console.log(`行高: ${textStyle.lineHeight || 'auto'}`)
106
+ console.log(`颜色: ${textStyle.textColor}`)
107
+ console.log(`对齐: ${textStyle.alignment}`)
108
+ }
109
+ }
package/common/i18n.md DELETED
@@ -1,385 +0,0 @@
1
- # 国际化 (i18n) 最佳实践
2
-
3
- 适用于需要多语言支持的前端项目
4
-
5
- ## 🎯 核心原则
6
-
7
- 1. **零硬编码**: 所有用户可见文本必须使用翻译函数
8
- 2. **键名规范**: 使用点分层级结构 (`common.save`, `user.profile.title`)
9
- 3. **参数化**: 动态内容使用参数插值,不拼接字符串
10
- 4. **回退机制**: 提供默认语言作为回退
11
-
12
- ## 🔧 标准配置 (Vue I18n)
13
-
14
- ### 目录结构
15
- ```
16
- src/locales/
17
- ├── index.ts # i18n 初始化
18
- ├── zh-CN.ts # 简体中文
19
- ├── en-US.ts # 英文
20
- └── ja-JP.ts # 日文
21
- ```
22
-
23
- ### 翻译文件示例
24
- ```typescript
25
- // zh-CN.ts
26
- export default {
27
- common: {
28
- save: '保存',
29
- cancel: '取消',
30
- confirm: '确认',
31
- delete: '删除',
32
- edit: '编辑',
33
- search: '搜索'
34
- },
35
- user: {
36
- profile: {
37
- title: '用户资料',
38
- name: '姓名',
39
- email: '邮箱'
40
- }
41
- },
42
- message: {
43
- saveSuccess: '保存成功',
44
- deleteConfirm: '确认删除 {name} 吗?',
45
- itemsSelected: '已选择 {count} 项'
46
- }
47
- }
48
- ```
49
-
50
- ## 🎨 单文件 i18n 配置(轻量方案)
51
-
52
- **适用于不想引入 vue-i18n 插件的项目,使用自定义实现:**
53
-
54
- ### 1. 配置位置
55
- - **翻译文件**: `src/locales/messages.ts` (键值对数组格式)
56
- - **语言状态**: `src/stores/state/locale.ts` (Pinia Store)
57
- - **全局注册**: `src/main.ts` (自定义 $t 方法)
58
-
59
- ### 2. 翻译文件格式
60
- ```typescript
61
- // src/locales/messages.ts
62
- // 索引 0 为英文,索引 1 为中文
63
-
64
- const messages: Record<string, [string, string]> = {
65
- // ========== 通用 ==========
66
- 是: ['Yes', '是'],
67
- 否: ['No', '否'],
68
- 确定: ['OK', '确定'],
69
- 取消: ['Cancel', '取消'],
70
- 确认: ['Confirm', '确认'],
71
- 保存: ['Save', '保存'],
72
- 提交: ['Submit', '提交'],
73
- 搜索: ['Search', '搜索'],
74
- 查询: ['Query', '查询'],
75
- 重置: ['Reset', '重置'],
76
- 导出: ['Export', '导出'],
77
- 新增: ['Add', '新增'],
78
- 编辑: ['Edit', '编辑'],
79
- 删除: ['Delete', '删除'],
80
- 详情: ['Detail', '详情'],
81
- 操作: ['Operation', '操作'],
82
- 状态: ['Status', '状态'],
83
- 备注: ['Remark', '备注'],
84
-
85
- // ========== 业务模块 ==========
86
- 订单号: ['Order No', '订单号'],
87
- 金额: ['Amount', '金额'],
88
- // ... 按模块分组
89
- }
90
-
91
- export default messages
92
- ```
93
-
94
- ### 3. 语言状态管理
95
- ```typescript
96
- // src/stores/state/locale.ts
97
- import { ref } from 'vue'
98
- import { defineStore } from 'pinia'
99
-
100
- export const useLocaleStore = defineStore('locale', () => {
101
- // 默认中文,可从 localStorage 读取
102
- const locale = ref<'zh-cn' | 'en'>(
103
- (localStorage.getItem('locale') as 'zh-cn' | 'en') || 'zh-cn'
104
- )
105
-
106
- const setLocale = (lang: 'zh-cn' | 'en') => {
107
- locale.value = lang
108
- localStorage.setItem('locale', lang)
109
- }
110
-
111
- const toggleLocale = () => {
112
- setLocale(locale.value === 'zh-cn' ? 'en' : 'zh-cn')
113
- }
114
-
115
- return { locale, setLocale, toggleLocale }
116
- })
117
- ```
118
-
119
- ### 4. main.ts 全局注册
120
- ```typescript
121
- // src/main.ts
122
- import { createApp } from 'vue'
123
- import { createPinia } from 'pinia'
124
- import App from './App.vue'
125
- import messages from '@/locales/messages'
126
- import { useLocaleStore } from '@/stores'
127
-
128
- const app = createApp(App)
129
- const pinia = createPinia()
130
-
131
- // ⚠️ 必须先注册 pinia,再使用 store
132
- app.use(pinia)
133
-
134
- // 获取语言状态
135
- const localeStore = useLocaleStore()
136
-
137
- // 注册全局 $t 方法
138
- app.config.globalProperties.$t = (key: string): string => {
139
- const translation = messages[key]
140
- if (!translation) {
141
- console.warn(`[i18n] Missing translation: ${key}`)
142
- return key
143
- }
144
- // 索引 0 为英文,索引 1 为中文
145
- return translation[localeStore.locale === 'zh-cn' ? 1 : 0] || key
146
- }
147
-
148
- app.mount('#app')
149
- ```
150
-
151
- ### 5. 使用方式
152
-
153
- **在模板中(直接使用):**
154
- ```vue
155
- <template>
156
- <el-button>{{ $t('保存') }}</el-button>
157
- <el-table-column :label="$t('订单号')" prop="orderNo" />
158
- <el-tag>{{ $t(row.status === 'success' ? '成功' : '失败') }}</el-tag>
159
- </template>
160
- ```
161
-
162
- **在 script 中(需要获取实例):**
163
- ```typescript
164
- <script setup lang="ts">
165
- import { getCurrentInstance } from 'vue'
166
-
167
- // ⚠️ 必须通过 getCurrentInstance 获取
168
- const { appContext } = getCurrentInstance()!
169
- const $t = appContext.config.globalProperties.$t
170
-
171
- // 使用
172
- ElMessage.success($t('保存成功'))
173
- ElMessageBox.confirm($t('确认删除吗?'), $t('提示'))
174
-
175
- // 表单验证
176
- const rules = {
177
- name: [{ required: true, message: $t('请输入名称'), trigger: 'blur' }]
178
- }
179
- </script>
180
- ```
181
-
182
- ### 6. 添加新翻译键
183
-
184
- 在 `src/locales/messages.ts` 中按分类添加:
185
-
186
- ```typescript
187
- const messages: Record<string, [string, string]> = {
188
- // ========== 已有内容 ==========
189
-
190
- // ========== 新增:交易模块 ==========
191
- 汇款订单: ['Remittance Order', '汇款订单'],
192
- 收款订单: ['Collection Order', '收款订单'],
193
- 交易流水: ['Transaction Record', '交易流水'],
194
-
195
- // ========== 新增:客户模块 ==========
196
- 客户名称: ['Customer Name', '客户名称'],
197
- 联系方式: ['Contact', '联系方式'],
198
- }
199
- ```
200
-
201
- ### ❌ 禁止事项
202
-
203
- ```typescript
204
- // ❌ 不要引入 useI18n(项目未使用 vue-i18n)
205
- import { useI18n } from 'vue-i18n'
206
-
207
- // ❌ 不要硬编码文本
208
- <el-button>保存</el-button>
209
-
210
- // ❌ 不要字符串拼接
211
- const msg = '删除' + name + '成功'
212
-
213
- // ❌ 不要在翻译值中使用变量(当前实现不支持)
214
- $t('已选择 {count} 项', { count: 5 }) // 不支持!
215
- // ✅ 替代方案
216
- `${$t('已选择')} ${count} ${$t('项')}`
217
- ```
218
-
219
- ## ✅ 最佳实践
220
-
221
- ### Vue 组件中使用
222
- ```vue
223
- <script setup lang="ts">
224
- import { getCurrentInstance } from 'vue'
225
-
226
- // 获取 $t 函数
227
- const { appContext } = getCurrentInstance()!
228
- const $t = appContext.config.globalProperties.$t
229
- </script>
230
-
231
- <template>
232
- <!-- ✅ 好 -->
233
- <el-button>{{ $t('common.save') }}</el-button>
234
- <el-table-column :label="$t('user.profile.name')" />
235
-
236
- <!-- 参数插值 -->
237
- <p>{{ $t('message.deleteConfirm', { name: userName }) }}</p>
238
-
239
- <!-- 复数处理 -->
240
- <span>{{ $t('message.itemsSelected', { count: selectedCount }) }}</span>
241
-
242
- <!-- ❌ 坏 -->
243
- <el-button>保存</el-button>
244
- <p>确认删除 {{ userName }} 吗?</p>
245
- </template>
246
- ```
247
-
248
- ### TypeScript 中使用
249
- ```typescript
250
- import { ElMessage } from 'element-plus'
251
-
252
- // ✅ 好
253
- ElMessage.success($t('message.saveSuccess'))
254
-
255
- // 带参数
256
- ElMessage.warning($t('message.deleteConfirm', { name: user.name }))
257
-
258
- // ❌ 坏
259
- ElMessage.success('保存成功')
260
- ```
261
-
262
- ### 动态文本映射
263
- ```typescript
264
- // ✅ 好 - 使用映射对象
265
- const statusMap = {
266
- pending: $t('status.pending'),
267
- success: $t('status.success'),
268
- error: $t('status.error')
269
- }
270
-
271
- const statusText = statusMap[status]
272
-
273
- // 或在模板中
274
- {{ { 0: $t('type.input'), 1: $t('type.output') }[row.type] }}
275
-
276
- // ❌ 坏 - 硬编码
277
- const statusMap = {
278
- pending: '待处理',
279
- success: '成功',
280
- error: '失败'
281
- }
282
- ```
283
-
284
- ## ⚠️ 禁止模式
285
-
286
- - ❌ 硬编码中文/英文文本
287
- - ❌ 字符串拼接: `'删除' + name + '吗?'`
288
- - ❌ 在代码中切换语言逻辑: `if (lang === 'zh') { ... }`
289
- - ❌ 重复的翻译键
290
-
291
- ## 📋 代码审查清单
292
-
293
- - [ ] 所有用户可见文本使用 `$t()`
294
- - [ ] 翻译键使用点分层级结构
295
- - [ ] 动态内容使用参数插值
296
- - [ ] 所有语言文件有相同的键结构
297
- - [ ] ElMessage/ElMessageBox 的文本已翻译
298
- - [ ] 表单验证消息已翻译
299
-
300
- ## 🔧 常见场景
301
-
302
- ### 表单验证
303
- ```typescript
304
- const rules = {
305
- name: [
306
- {
307
- required: true,
308
- message: $t('validation.required', { field: $t('user.profile.name') }),
309
- trigger: 'blur'
310
- },
311
- {
312
- min: 2,
313
- max: 20,
314
- message: $t('validation.length', { min: 2, max: 20 }),
315
- trigger: 'blur'
316
- }
317
- ]
318
- }
319
- ```
320
-
321
- ### 确认对话框
322
- ```typescript
323
- const handleDelete = async (row: User) => {
324
- try {
325
- await ElMessageBox.confirm(
326
- $t('message.deleteConfirm', { name: row.name }),
327
- $t('common.warning'),
328
- {
329
- confirmButtonText: $t('common.confirm'),
330
- cancelButtonText: $t('common.cancel'),
331
- type: 'warning'
332
- }
333
- )
334
- // 执行删除
335
- } catch (err) {
336
- // 用户取消
337
- }
338
- }
339
- ```
340
-
341
- ### 时间格式化
342
- ```typescript
343
- // 使用 i18n 的日期格式化
344
- const formatDate = (date: Date) => {
345
- return new Intl.DateTimeFormat(locale.value, {
346
- year: 'numeric',
347
- month: 'long',
348
- day: 'numeric'
349
- }).format(date)
350
- }
351
- ```
352
-
353
- ## 🌍 语言切换
354
-
355
- ```typescript
356
- import { useI18n } from 'vue-i18n'
357
-
358
- const { locale } = useI18n()
359
-
360
- // 切换语言
361
- const changeLanguage = (lang: 'zh-CN' | 'en-US') => {
362
- locale.value = lang
363
- localStorage.setItem('language', lang)
364
- }
365
- ```
366
-
367
- ---
368
-
369
- ## ⚠️ 重要:配置文件管理
370
-
371
- ### Copilot 配置 .gitignore
372
-
373
- **推荐做法:**将自动生成的 `.github/copilot-instructions.md` 添加到 `.gitignore`
374
-
375
- ```gitignore
376
- # Copilot 配置(自动生成)
377
- .github/copilot-instructions.md
378
- ```
379
-
380
- **适用项目:**
381
- - 需要国际化的 Web 应用
382
- - Vue.js、React、Angular 项目
383
- - 多语言支持的前端项目
384
-
385
- **详细指南**: 参考 [Copilot .gitignore 通用指南](../docs/guides/COPILOT_GITIGNORE_GUIDE.md)