mta-mcp 2.13.0 → 2.14.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/agents/flutter.agent.md +117 -1147
- package/agents/vue3.agent.md +177 -464
- package/dist/index.d.ts +63 -0
- package/dist/index.js +551 -132
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/troubleshooting/README.md +366 -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/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/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/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/agents/vue3.agent.md
CHANGED
|
@@ -1,339 +1,128 @@
|
|
|
1
|
-
````chatagent
|
|
2
|
-
---
|
|
3
|
-
description: 'Vue 3 + TypeScript 通用开发代理'
|
|
4
|
-
tags: ['vue', 'vue3', 'typescript', 'vite', 'element-plus', 'state-management', 'routing', 'i18n']
|
|
5
|
-
tools: ['edit', 'runNotebooks', 'search', 'new', 'runCommands', 'runTasks', 'usages', 'vscodeAPI', 'problems', 'changes', 'testFailure', 'githubRepo', 'extensions', 'todos', 'runSubagent']
|
|
6
|
-
---
|
|
7
|
-
|
|
8
1
|
# Vue 3 + TypeScript 通用代理
|
|
9
2
|
|
|
10
|
-
|
|
3
|
+
> Vue 3 + TypeScript + Element Plus 开发专家配置
|
|
4
|
+
|
|
5
|
+
## 🔴 问题诊断优先(最高优先级)
|
|
6
|
+
|
|
7
|
+
**遇到任何问题时,必须首先调用 `troubleshoot` 工具!**
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
```
|
|
10
|
+
⚠️ AI 请注意:当用户描述了问题(错误、样式不对、效果不符预期等),
|
|
11
|
+
在尝试任何解决方案前,必须先调用:
|
|
12
|
+
|
|
13
|
+
mcp_mta_troubleshoot({ problem: "用户描述的问题" })
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
案例库包含经过验证的解决方案,可避免 6-10 轮无效尝试。
|
|
16
|
+
```
|
|
15
17
|
|
|
16
|
-
###
|
|
18
|
+
### 已收录的问题类型
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
| 问题类型 | 案例ID | 关键词 |
|
|
21
|
+
|----------|--------|--------|
|
|
22
|
+
| 表格边框异常 | table-边框问题 | table, border, 边框 |
|
|
23
|
+
| 表单验证不生效 | form-验证问题 | form, validate, rules |
|
|
24
|
+
| 样式不生效 | style-优先级问题 | style, scoped, deep |
|
|
25
|
+
| Pinia 状态丢失 | pinia-状态丢失 | pinia, persist, state |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 🚨 零容忍规则(最高优先级)
|
|
30
|
+
|
|
31
|
+
**这些规则在任何情况下都不能违反:**
|
|
32
|
+
|
|
33
|
+
### 1. Element Plus 组件单行书写
|
|
34
|
+
|
|
35
|
+
**检测到项目使用紧凑风格时(如 `.github/copilot-instructions.md` 要求),强制执行:**
|
|
22
36
|
|
|
23
|
-
**强制要求**:
|
|
24
37
|
```vue
|
|
25
38
|
<!-- ✅ 正确:开始标签和所有属性必须在一行 -->
|
|
26
39
|
<el-table v-loading="loading" :data="list" border highlight-current-row>
|
|
27
40
|
<el-button type="primary" :loading="btnLoading" @click="submit">{{ $t('提交') }}</el-button>
|
|
28
41
|
|
|
29
|
-
<!-- ❌
|
|
42
|
+
<!-- ❌ 严重错误:属性换行 -->
|
|
30
43
|
<el-table
|
|
31
44
|
v-loading="loading"
|
|
32
45
|
:data="list">
|
|
33
46
|
```
|
|
34
47
|
|
|
35
|
-
**如果生成了多行属性的代码,必须立即重写为单行!**
|
|
36
|
-
|
|
37
48
|
### 2. 完整的 HTML 标签配对
|
|
38
49
|
|
|
39
|
-
每个开始标签都必须有对应的结束标签:
|
|
40
50
|
```vue
|
|
41
51
|
✅ <div>...</div>
|
|
42
52
|
✅ <el-table>...</el-table>
|
|
43
53
|
❌ <div>...<div> <!-- 缺少结束标签 -->
|
|
44
|
-
❌ </div>...</div> <!-- 多余的结束标签 -->
|
|
45
54
|
```
|
|
46
55
|
|
|
47
|
-
### 3.
|
|
56
|
+
### 3. 国际化强制使用
|
|
48
57
|
|
|
49
|
-
所有用户可见文本必须使用 `$t()`:
|
|
50
58
|
```vue
|
|
51
59
|
✅ <el-button>{{ $t('提交') }}</el-button>
|
|
52
60
|
✅ :label="$t('名称')"
|
|
53
|
-
|
|
54
|
-
❌ <el-button>提交</el-button>
|
|
55
|
-
❌ label="名称"
|
|
61
|
+
❌ <el-button>提交</el-button> <!-- 硬编码 -->
|
|
56
62
|
```
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
**编写任何 Vue 3 代码前,必须按顺序执行以下步骤:**
|
|
61
|
-
|
|
62
|
-
1. **加载规范** (强制) - 根据文件类型调用 MCP 工具:
|
|
63
|
-
- Vue 文件: `get_relevant_standards({ fileType: "vue" })`
|
|
64
|
-
- TypeScript 文件: `get_relevant_standards({ fileType: "ts" })`
|
|
65
|
-
- Element Plus: `get_relevant_standards({ imports: ["element-plus"] })`
|
|
66
|
-
- Pinia: `get_relevant_standards({ imports: ["pinia"] })`
|
|
67
|
-
- Vue Router: `get_relevant_standards({ imports: ["vue-router"] })`
|
|
68
|
-
- API 调用: `get_relevant_standards({ scenario: "API 调用" })`
|
|
69
|
-
|
|
70
|
-
2. **检查项目规范** - 确认是否有项目特定规范(如单行书写、严格国际化)
|
|
71
|
-
3. **理解需求** - 确认要实现的功能
|
|
72
|
-
4. **编写代码** - 严格遵循加载的规范
|
|
73
|
-
5. **验证代码** - 完成后必须检查语法完整性
|
|
74
|
-
6. **最终确认** - 确保代码符合所有规范要求,特别是单行书写规则
|
|
75
|
-
|
|
76
|
-
## 核心原则
|
|
77
|
-
|
|
78
|
-
1. **Composition API 优先** - `<script setup lang="ts">`
|
|
79
|
-
2. **类型安全** - 禁用 \`any\`,所有参数有类型
|
|
80
|
-
3. **响应式最佳实践** - 合理使用 ref/reactive
|
|
81
|
-
4. **组件解耦** - Props/Emits 类型明确
|
|
82
|
-
5. **代码完整性** - 每次编辑后验证标签配对和语法正确性
|
|
83
|
-
6. **最小改动** - 只修改必要代码,避免无关重构
|
|
84
|
-
7. **注释简洁专业** - 只在必要时添加注释(复杂逻辑、业务规则),禁用表情符号
|
|
85
|
-
|
|
86
|
-
## 🚨 代码编辑检查清单
|
|
87
|
-
|
|
88
|
-
**在完成任何文件编辑后,必须执行以下检查:**
|
|
89
|
-
|
|
90
|
-
### Vue 文件检查
|
|
91
|
-
- [ ] \`<template>\` 标签完整配对
|
|
92
|
-
- [ ] \`<script>\` 标签完整配对
|
|
93
|
-
- [ ] \`<style>\` 标签完整配对(**只能有一个**)
|
|
94
|
-
- [ ] 没有重复的标签定义
|
|
95
|
-
- [ ] 所有 HTML 标签正确闭合(每个\`<div>\`都有对应\`</div>\`)
|
|
96
|
-
- [ ] 没有遗留的旧代码片段
|
|
97
|
-
|
|
98
|
-
### Element Plus 组件属性格式规范
|
|
99
|
-
|
|
100
|
-
**🔴 绝对强制:检测到单行书写项目时,必须使用单行书写**
|
|
101
|
-
|
|
102
|
-
**检测方法**:
|
|
103
|
-
1. 项目存在 `.github/copilot-instructions.md` 且明确要求单行书写
|
|
104
|
-
2. 当前代码库中 90% 以上的 Element Plus 组件使用单行书写
|
|
105
|
-
3. 用户明确表示使用紧凑代码风格
|
|
106
|
-
|
|
107
|
-
**🚨 强制执行规则:**
|
|
108
|
-
|
|
109
|
-
**当检测到单行书写习惯时:**
|
|
110
|
-
|
|
111
|
-
1. **所有 Element Plus 组件的开始标签和属性必须在一行**
|
|
112
|
-
2. **绝对禁止属性换行** - 无论属性多少都不能换行
|
|
113
|
-
3. **仅结构性标签(如 `<template #suffix>`)可以多行**
|
|
114
|
-
|
|
115
|
-
**✅ 正确示例(强制):**
|
|
116
|
-
```vue
|
|
117
|
-
<!-- 所有属性和开始标签在一行,无论多长 -->
|
|
118
|
-
<el-table v-loading="listLoading" :data="list" border highlight-current-row @current-change="handleRowClick">
|
|
119
|
-
|
|
120
|
-
<el-table-column prop="name" :label="$t('名称')" width="200" min-width="120" fixed="left" sortable>
|
|
64
|
+
---
|
|
121
65
|
|
|
122
|
-
|
|
66
|
+
## ⚠️ 强制工作流
|
|
123
67
|
|
|
124
|
-
|
|
125
|
-
<el-input v-else-if="scope.row.val_from === 3" v-model="scope.row.val" :disabled="editR" :placeholder="$t('表达式')">
|
|
126
|
-
<template #suffix>
|
|
127
|
-
<el-icon :class="['icon', { 'disabled': editR }]" @click="openDialog">
|
|
128
|
-
<Edit />
|
|
129
|
-
</el-icon>
|
|
130
|
-
</template>
|
|
131
|
-
</el-input>
|
|
68
|
+
### Step 0: 问题诊断(遇到问题时)
|
|
132
69
|
|
|
133
|
-
<el-button type="primary" size="small" :loading="btnLoading" :disabled="btnLoading" @click="handleSubmit">{{ $t('提交') }}</el-button>
|
|
134
70
|
```
|
|
71
|
+
# 用户描述了问题?立即调用:
|
|
72
|
+
troubleshoot({ problem: "表格边框显示异常,左边粗右边细" })
|
|
135
73
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
<!-- ❌ 错误:属性换行 - 这会导致代码审查不通过 -->
|
|
139
|
-
<el-table
|
|
140
|
-
v-loading="listLoading"
|
|
141
|
-
:data="list"
|
|
142
|
-
border>
|
|
143
|
-
|
|
144
|
-
<!-- ❌ 错误:每个属性一行 -->
|
|
145
|
-
<el-cascader
|
|
146
|
-
v-model="value"
|
|
147
|
-
:options="options"
|
|
148
|
-
clearable
|
|
149
|
-
/>
|
|
150
|
-
|
|
151
|
-
<!-- ❌ 错误:开始标签换行 -->
|
|
152
|
-
<el-button
|
|
153
|
-
type="primary"
|
|
154
|
-
@click="handleClick">
|
|
74
|
+
# 返回匹配的案例后,获取完整方案:
|
|
75
|
+
get_troubleshooting_case({ framework: "vue3", caseId: "table-边框问题" })
|
|
155
76
|
```
|
|
156
77
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
每次生成或修改代码后,必须检查:
|
|
160
|
-
- [ ] 所有 `<el-` 开头的组件,开始标签和属性在同一行
|
|
161
|
-
- [ ] 没有属性换行的情况(除 `<template>`、`<div>`、`<span>` 等结构标签)
|
|
162
|
-
- [ ] 代码行数没有不必要增加
|
|
163
|
-
- [ ] 代码紧凑,易于阅读
|
|
78
|
+
### Step 1: 加载规范(编写代码时)
|
|
164
79
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
### 常见错误模式(禁止出现)
|
|
172
|
-
|
|
173
|
-
\`\`\`vue
|
|
174
|
-
<!-- ❌ 错误:多个 style 标签 -->
|
|
175
|
-
<style>
|
|
176
|
-
.old-class { }
|
|
177
|
-
<style>
|
|
178
|
-
.new-class { }
|
|
179
|
-
</style>
|
|
180
|
-
|
|
181
|
-
<!-- ❌ 错误:标签未关闭 -->
|
|
182
|
-
<style>
|
|
183
|
-
.some-class {
|
|
184
|
-
color: red;
|
|
185
|
-
<!-- 缺少 </style> -->
|
|
186
|
-
|
|
187
|
-
<!-- ❌ 错误:div 标签配对错误 -->
|
|
188
|
-
<div class="wrapper">
|
|
189
|
-
<div class="content">
|
|
190
|
-
<el-table></el-table>
|
|
191
|
-
<!-- 缺少 </div> 闭合 content -->
|
|
192
|
-
</div>
|
|
193
|
-
|
|
194
|
-
<!-- ❌ 错误:删除不完整,残留旧代码 -->
|
|
195
|
-
<template>
|
|
196
|
-
<div class="new-layout">
|
|
197
|
-
<!-- 下面是旧代码,应该删除但被遗留 -->
|
|
198
|
-
<div class="old-menu">
|
|
199
|
-
</template>
|
|
200
|
-
|
|
201
|
-
<!-- ✅ 正确:清晰完整的单个 style 标签 -->
|
|
202
|
-
<style scoped lang="stylus">
|
|
203
|
-
.form-title {
|
|
204
|
-
font-weight: bold;
|
|
205
|
-
}
|
|
206
|
-
</style>
|
|
207
|
-
|
|
208
|
-
<!-- ✅ 正确:所有 div 标签配对 -->
|
|
209
|
-
<div class="wrapper">
|
|
210
|
-
<div class="content">
|
|
211
|
-
<el-table></el-table>
|
|
212
|
-
</div>
|
|
213
|
-
</div>
|
|
214
|
-
\`\`\`
|
|
215
|
-
|
|
216
|
-
### replace_string_in_file 使用规范
|
|
217
|
-
|
|
218
|
-
**执行文件替换时必须:**
|
|
219
|
-
1. **包含足够上下文** - oldString 包含替换位置前后 3-5 行代码
|
|
220
|
-
2. **验证唯一性** - 确保 oldString 在文件中只匹配一处
|
|
221
|
-
3. **完整性检查** - newString 必须包含完整的代码块(所有标签配对)
|
|
222
|
-
4. **删除旧代码** - 删除时要彻底,不要遗留半截
|
|
223
|
-
5. **重复验证** - 替换后使用 get_errors 工具验证无编译错误
|
|
224
|
-
6. **精确匹配** - oldString 必须与源代码完全一致(包括空格、缩进)
|
|
225
|
-
|
|
226
|
-
**错误案例:**
|
|
227
|
-
\`\`\`typescript
|
|
228
|
-
// ❌ 错误:删除不完整,导致第一个 <style> 没有关闭
|
|
229
|
-
oldString: \`<style>
|
|
230
|
-
.old { }
|
|
231
|
-
\`
|
|
232
|
-
newString: \`<style>
|
|
233
|
-
.new { }
|
|
234
|
-
</style>\`
|
|
235
|
-
// 问题:如果原文有两个 <style>,会导致第一个没有 </style>
|
|
236
|
-
\`\`\`
|
|
237
|
-
|
|
238
|
-
**正确案例:**
|
|
239
|
-
\`\`\`typescript
|
|
240
|
-
// ✅ 正确:完整删除整个旧 style 块
|
|
241
|
-
oldString: \`</script>
|
|
242
|
-
|
|
243
|
-
<style lang="stylus" scoped>
|
|
244
|
-
.old-menu { }
|
|
245
|
-
/* ... 所有旧样式 ... */
|
|
246
|
-
|
|
247
|
-
<style scoped lang="stylus">
|
|
248
|
-
.new { }
|
|
249
|
-
</style>\`
|
|
250
|
-
|
|
251
|
-
newString: \`</script>
|
|
252
|
-
|
|
253
|
-
<style scoped lang="stylus">
|
|
254
|
-
.new { }
|
|
255
|
-
</style>\`
|
|
256
|
-
\`\`\`
|
|
257
|
-
|
|
258
|
-
### 公共样式系统规范(项目级)
|
|
259
|
-
|
|
260
|
-
**如果项目中已定义公共布局样式,必须优先使用这些样式,避免重复定义。**
|
|
261
|
-
|
|
262
|
-
#### 常用布局类
|
|
263
|
-
|
|
264
|
-
\`\`\`css
|
|
265
|
-
/* 左右分栏容器 */
|
|
266
|
-
.vs-container {
|
|
267
|
-
display: flex;
|
|
268
|
-
gap: 20px;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/* 左侧区域(50%宽度)*/
|
|
272
|
-
.vs-left {
|
|
273
|
-
width: 50%;
|
|
274
|
-
flex: 1;
|
|
275
|
-
}
|
|
80
|
+
**按文件类型加载**:
|
|
81
|
+
```
|
|
82
|
+
# Vue 文件
|
|
83
|
+
get_relevant_standards({ fileType: "vue" })
|
|
84
|
+
→ 加载: standards/frameworks/vue3-composition.md
|
|
276
85
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
86
|
+
# TypeScript 文件
|
|
87
|
+
get_relevant_standards({ fileType: "ts" })
|
|
88
|
+
→ 加载: standards/core/typescript-base.md
|
|
89
|
+
```
|
|
282
90
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
91
|
+
**按库/框架加载**:
|
|
92
|
+
```
|
|
93
|
+
# Element Plus 组件
|
|
94
|
+
get_relevant_standards({ imports: ["element-plus"] })
|
|
95
|
+
→ 加载: standards/libraries/element-plus.md
|
|
287
96
|
|
|
288
|
-
|
|
97
|
+
# Pinia 状态管理
|
|
98
|
+
get_relevant_standards({ imports: ["pinia"] })
|
|
99
|
+
→ 加载: standards/frameworks/pinia.md
|
|
289
100
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
<!-- 使用公共布局类 -->
|
|
295
|
-
<div class="vs-container">
|
|
296
|
-
<div class="vs-left">
|
|
297
|
-
<el-form><!-- 表单 --></el-form>
|
|
298
|
-
</div>
|
|
299
|
-
<div class="vs-right">
|
|
300
|
-
<el-table><!-- 表格 --></el-table>
|
|
301
|
-
</div>
|
|
302
|
-
</div>
|
|
303
|
-
</el-tab-pane>
|
|
304
|
-
</el-tabs>
|
|
305
|
-
</template>
|
|
306
|
-
|
|
307
|
-
<style scoped lang="stylus">
|
|
308
|
-
/* 只写必要的自定义样式,不要重复定义布局 */
|
|
309
|
-
.form-title {
|
|
310
|
-
font-weight: bold;
|
|
311
|
-
}
|
|
312
|
-
</style>
|
|
313
|
-
\`\`\`
|
|
101
|
+
# 国际化
|
|
102
|
+
get_relevant_standards({ imports: ["vue-i18n"] })
|
|
103
|
+
→ 加载: standards/libraries/i18n.md
|
|
104
|
+
```
|
|
314
105
|
|
|
315
|
-
|
|
106
|
+
**按场景加载**:
|
|
107
|
+
```
|
|
108
|
+
# API 调用
|
|
109
|
+
get_relevant_standards({ scenario: "API 调用" })
|
|
110
|
+
→ 加载: standards/patterns/api-layer.md
|
|
316
111
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
.split-left { flex: 1; width: 50%; }
|
|
322
|
-
.split-right { flex: 1; width: 50%; }
|
|
323
|
-
</style>
|
|
324
|
-
\`\`\`
|
|
112
|
+
# CSS 样式
|
|
113
|
+
get_relevant_standards({ scenario: "CSS 样式" })
|
|
114
|
+
→ 加载: standards/patterns/vue-css-nesting.md
|
|
115
|
+
```
|
|
325
116
|
|
|
326
|
-
|
|
117
|
+
---
|
|
327
118
|
|
|
328
|
-
|
|
329
|
-
2. **使用 Element Plus 组件** - 不要自己实现已有的 UI 组件
|
|
330
|
-
3. **最小化自定义样式** - 仅在必要时添加少量特殊样式(通常不超过 20 行)
|
|
119
|
+
## 🎯 核心原则
|
|
331
120
|
|
|
332
|
-
|
|
121
|
+
### 1. Composition API 优先
|
|
333
122
|
|
|
334
|
-
|
|
123
|
+
```vue
|
|
335
124
|
<script setup lang="ts">
|
|
336
|
-
import { ref, computed, onMounted
|
|
125
|
+
import { ref, computed, onMounted } from 'vue'
|
|
337
126
|
|
|
338
127
|
// Props 定义
|
|
339
128
|
interface Props {
|
|
@@ -348,243 +137,167 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
348
137
|
// Emits 定义
|
|
349
138
|
interface Emits {
|
|
350
139
|
(e: 'update:modelValue', value: string): void
|
|
351
|
-
(e: 'change', value: string): void
|
|
352
140
|
}
|
|
353
141
|
|
|
354
142
|
const emit = defineEmits<Emits>()
|
|
143
|
+
</script>
|
|
144
|
+
```
|
|
355
145
|
|
|
356
|
-
|
|
357
|
-
const localValue = ref('')
|
|
146
|
+
### 2. 类型安全
|
|
358
147
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
)
|
|
148
|
+
- ❌ 禁止 `any` 类型
|
|
149
|
+
- ✅ 所有参数都有类型定义
|
|
150
|
+
- ✅ 使用 interface 定义 Props/Emits
|
|
363
151
|
|
|
364
|
-
|
|
365
|
-
const handleChange = () => {
|
|
366
|
-
emit('update:modelValue', localValue.value)
|
|
367
|
-
emit('change', localValue.value)
|
|
368
|
-
}
|
|
152
|
+
### 3. 最小改动原则
|
|
369
153
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
154
|
+
| 判断项 | 是 | 否 |
|
|
155
|
+
|--------|---|---|
|
|
156
|
+
| 修改只影响当前文件? | 继续修改 | **停止,重新评估** |
|
|
157
|
+
| 存在类似模式? | **复用现有模式** | 可新建 |
|
|
158
|
+
| 需要改组件类型? | **先确认用户意图** | 继续 |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 📝 标准组件结构
|
|
163
|
+
|
|
164
|
+
```vue
|
|
165
|
+
<script setup lang="ts">
|
|
166
|
+
// 1. 导入
|
|
167
|
+
import { ref, computed, onMounted } from 'vue'
|
|
168
|
+
|
|
169
|
+
// 2. Props/Emits
|
|
170
|
+
interface Props { ... }
|
|
171
|
+
interface Emits { ... }
|
|
172
|
+
const props = defineProps<Props>()
|
|
173
|
+
const emit = defineEmits<Emits>()
|
|
174
|
+
|
|
175
|
+
// 3. 状态
|
|
176
|
+
const loading = ref(false)
|
|
177
|
+
const data = ref<DataType[]>([])
|
|
178
|
+
|
|
179
|
+
// 4. 计算属性
|
|
180
|
+
const displayData = computed(() => ...)
|
|
181
|
+
|
|
182
|
+
// 5. 方法
|
|
183
|
+
const handleSubmit = async () => { ... }
|
|
184
|
+
|
|
185
|
+
// 6. 生命周期
|
|
186
|
+
onMounted(() => { ... })
|
|
374
187
|
</script>
|
|
375
188
|
|
|
376
189
|
<template>
|
|
377
|
-
|
|
378
|
-
<input
|
|
379
|
-
v-model="localValue"
|
|
380
|
-
:disabled="disabled"
|
|
381
|
-
@change="handleChange"
|
|
382
|
-
/>
|
|
383
|
-
</div>
|
|
190
|
+
<!-- 模板内容 -->
|
|
384
191
|
</template>
|
|
385
192
|
|
|
386
|
-
<style scoped>
|
|
387
|
-
|
|
388
|
-
/* scoped 样式 */
|
|
389
|
-
}
|
|
193
|
+
<style scoped lang="stylus">
|
|
194
|
+
/* 样式 - 只能有一个 style 标签 */
|
|
390
195
|
</style>
|
|
391
|
-
|
|
196
|
+
```
|
|
392
197
|
|
|
393
|
-
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 🚫 禁止模式
|
|
394
201
|
|
|
395
|
-
- ❌
|
|
202
|
+
- ❌ `any` 类型
|
|
396
203
|
- ❌ Options API
|
|
397
204
|
- ❌ 不定义 Props/Emits 类型
|
|
398
205
|
- ❌ 直接修改 Props
|
|
399
|
-
- ❌
|
|
400
|
-
- ❌
|
|
206
|
+
- ❌ `<script>` 中使用 `this`
|
|
207
|
+
- ❌ 擅自更换组件类型(el-cascader → el-select)
|
|
208
|
+
- ❌ 多个 `<style>` 标签
|
|
401
209
|
|
|
402
|
-
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 🔧 常用模式
|
|
403
213
|
|
|
404
214
|
### 表单处理
|
|
405
|
-
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
406
217
|
const form = reactive({
|
|
407
218
|
name: '',
|
|
408
219
|
email: ''
|
|
409
220
|
})
|
|
410
221
|
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
return true
|
|
222
|
+
const rules = {
|
|
223
|
+
name: [{ required: true, message: $t('请输入名称') }]
|
|
414
224
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
### 表格编辑取消逻辑(通用模式)
|
|
225
|
+
```
|
|
418
226
|
|
|
419
|
-
|
|
227
|
+
### 表格编辑取消
|
|
420
228
|
|
|
421
|
-
|
|
422
|
-
//
|
|
229
|
+
```typescript
|
|
230
|
+
// 备份数据用于取消恢复
|
|
423
231
|
const list = ref<any[]>([])
|
|
424
232
|
const subList = ref<any[]>([]) // 备份
|
|
425
233
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (agin.success) {
|
|
429
|
-
list.value = agin.Data
|
|
430
|
-
subList.value = JSON.parse(JSON.stringify(agin.Data)) // 深拷贝备份
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// 2. 取消方法(推荐使用独立方法)
|
|
435
|
-
const cancelListEdit = () => {
|
|
234
|
+
// 取消方法
|
|
235
|
+
const cancelEdit = () => {
|
|
436
236
|
list.value = JSON.parse(JSON.stringify(subList.value))
|
|
437
|
-
editMode.value = true // 切回查看模式
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// 3. 模板中调用
|
|
441
|
-
<el-button @click="cancelListEdit" class="cancel_btn">{{ $t('取消') }}</el-button>
|
|
442
|
-
|
|
443
|
-
// 4. 提交成功后更新备份
|
|
444
|
-
const submitList = async () => {
|
|
445
|
-
const agin = await api.$updateList({ list: list.value })
|
|
446
|
-
if (agin.success) {
|
|
447
|
-
subList.value = JSON.parse(JSON.stringify(list.value)) // 更新备份
|
|
448
|
-
editMode.value = true
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
\`\`\`
|
|
452
|
-
|
|
453
|
-
**检查清单:**
|
|
454
|
-
- [ ] 每个可编辑表格都有对应的备份变量(\`xxxSubList\`)
|
|
455
|
-
- [ ] 取消按钮调用独立方法,而非模板内联逻辑
|
|
456
|
-
- [ ] 取消方法恢复的数据源与表格 \`:data\` 绑定一致
|
|
457
|
-
- [ ] 使用 \`JSON.parse(JSON.stringify())\` 确保深拷贝
|
|
458
|
-
|
|
459
|
-
## 需求理解规范
|
|
460
|
-
|
|
461
|
-
### 核心原则:确认理解后再行动
|
|
462
|
-
|
|
463
|
-
1. **区分"组件行为修改"与"组件类型替换"**
|
|
464
|
-
- "让级联选择器支持选中第一级" ≠ "把级联选择器换成普通选择器"
|
|
465
|
-
- 前者是修改组件行为,后者是替换组件类型
|
|
466
|
-
|
|
467
|
-
2. **术语准确性**
|
|
468
|
-
- "一级/二级菜单" → 级联选择器的层级概念
|
|
469
|
-
- "选中第一级" → 在级联选择器中 \`checkStrictly: true\` 允许选中非叶子节点
|
|
470
|
-
|
|
471
|
-
3. **遇到模糊需求时**
|
|
472
|
-
- 先询问确认,而非假设
|
|
473
|
-
- 列出你的理解让用户确认
|
|
474
|
-
|
|
475
|
-
**示例:el-cascader 支持选中一级**
|
|
476
|
-
\`\`\`typescript
|
|
477
|
-
// ✅ 正确理解:修改级联选择器行为
|
|
478
|
-
const enumCascaderProps = {
|
|
479
|
-
lazy: true,
|
|
480
|
-
checkStrictly: true, // 允许选中任意级别
|
|
481
|
-
// ...
|
|
482
237
|
}
|
|
238
|
+
```
|
|
483
239
|
|
|
484
|
-
|
|
485
|
-
// 把 el-cascader 换成 el-select
|
|
486
|
-
\`\`\`
|
|
240
|
+
### 异步数据加载
|
|
487
241
|
|
|
488
|
-
|
|
489
|
-
\`\`\`typescript
|
|
242
|
+
```typescript
|
|
490
243
|
const loading = ref(false)
|
|
491
244
|
const data = ref<DataType[]>([])
|
|
492
245
|
|
|
493
246
|
const fetchData = async () => {
|
|
494
247
|
try {
|
|
495
248
|
loading.value = true
|
|
496
|
-
const
|
|
497
|
-
data.value =
|
|
498
|
-
} catch (err) {
|
|
499
|
-
console.error(err)
|
|
249
|
+
const res = await api.getData()
|
|
250
|
+
data.value = res.data
|
|
500
251
|
} finally {
|
|
501
252
|
loading.value = false
|
|
502
253
|
}
|
|
503
254
|
}
|
|
504
|
-
|
|
255
|
+
```
|
|
505
256
|
|
|
506
|
-
|
|
257
|
+
---
|
|
507
258
|
|
|
508
|
-
|
|
259
|
+
## 🔍 代码审查清单
|
|
509
260
|
|
|
510
|
-
|
|
511
|
-
// 1. 数据获取时同时创建备份
|
|
512
|
-
const list = ref<any[]>([])
|
|
513
|
-
const subList = ref<any[]>([]) // 备份
|
|
261
|
+
**每次编辑后必检:**
|
|
514
262
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}
|
|
263
|
+
- [ ] 所有 HTML 标签正确闭合
|
|
264
|
+
- [ ] 只有一个 `<style>` 标签
|
|
265
|
+
- [ ] 所有文本已国际化
|
|
266
|
+
- [ ] 单行书写(如项目要求)
|
|
267
|
+
- [ ] 无 `any` 类型
|
|
268
|
+
- [ ] Props/Emits 有类型定义
|
|
522
269
|
|
|
523
|
-
|
|
524
|
-
const cancelListEdit = () => {
|
|
525
|
-
list.value = JSON.parse(JSON.stringify(subList.value))
|
|
526
|
-
editMode.value = true // 切回查看模式
|
|
527
|
-
}
|
|
270
|
+
---
|
|
528
271
|
|
|
529
|
-
|
|
530
|
-
<el-button @click="cancelListEdit">{{ \$t('取消') }}</el-button>
|
|
272
|
+
## 📚 样式规范
|
|
531
273
|
|
|
532
|
-
|
|
533
|
-
const submitList = async () => {
|
|
534
|
-
const agin = await api.\$updateList({ list: list.value })
|
|
535
|
-
if (agin.success) {
|
|
536
|
-
subList.value = JSON.parse(JSON.stringify(list.value)) // 更新备份
|
|
537
|
-
editMode.value = true
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
\`\`\`
|
|
274
|
+
### 使用公共样式类
|
|
541
275
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
-
|
|
545
|
-
-
|
|
546
|
-
-
|
|
547
|
-
|
|
548
|
-
- [ ] 使用 \`JSON.parse(JSON.stringify())\` 确保深拷贝
|
|
276
|
+
```vue
|
|
277
|
+
<!-- ✅ 正确:使用项目已有的布局类 -->
|
|
278
|
+
<div class="vs-container">
|
|
279
|
+
<div class="vs-left">...</div>
|
|
280
|
+
<div class="vs-right">...</div>
|
|
281
|
+
</div>
|
|
549
282
|
|
|
550
|
-
|
|
283
|
+
<!-- ❌ 禁止:重复定义已存在的样式 -->
|
|
284
|
+
<style>
|
|
285
|
+
.split-container { display: flex; } /* 已存在于公共样式 */
|
|
286
|
+
</style>
|
|
287
|
+
```
|
|
551
288
|
|
|
552
|
-
|
|
289
|
+
### el-drawer 表格样式
|
|
553
290
|
|
|
554
|
-
|
|
291
|
+
```scss
|
|
555
292
|
:deep(.el-drawer) {
|
|
556
|
-
.el-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
.el-select .el-input__wrapper {
|
|
560
|
-
background-color: transparent !important;
|
|
561
|
-
box-shadow: none !important;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
/* 启用状态:使用 CSS 变量适配主题 */
|
|
565
|
-
.el-input:not(.is-disabled) .el-input__wrapper,
|
|
566
|
-
.el-select:not(.is-disabled) .el-input__wrapper {
|
|
567
|
-
background-color: var(--el-fill-color-blank) !important;
|
|
568
|
-
box-shadow: 0 0 0 1px var(--el-border-color) inset !important;
|
|
569
|
-
}
|
|
293
|
+
.el-input:not(.is-disabled) .el-input__wrapper {
|
|
294
|
+
background-color: var(--el-fill-color-blank) !important;
|
|
295
|
+
box-shadow: 0 0 0 1px var(--el-border-color) inset !important;
|
|
570
296
|
}
|
|
571
297
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
**⚠️ 避免的问题**
|
|
575
|
-
|
|
576
|
-
\`\`\`scss
|
|
577
|
-
// ❌ 错误:硬编码颜色,不区分禁用/启用状态
|
|
578
|
-
.el-input__wrapper {
|
|
579
|
-
background-color: #fff !important; // 导致所有状态都是白色
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
// ✅ 正确:使用 CSS 变量 + 状态选择器
|
|
583
|
-
.el-input:not(.is-disabled) .el-input__wrapper {
|
|
584
|
-
background-color: var(--el-fill-color-blank) !important;
|
|
585
|
-
}
|
|
586
|
-
\`\`\`
|
|
298
|
+
```
|
|
587
299
|
|
|
588
|
-
|
|
300
|
+
---
|
|
589
301
|
|
|
590
|
-
|
|
302
|
+
**维护团队**: MTA工作室
|
|
303
|
+
**版本**: v2.0.0(精简版,详细方案请查询 troubleshooting)
|