8.3 KiB
name, version, description, requires
| name | version | description | requires | |
|---|---|---|---|---|
| component-scaffold | 4.1.0 | 生成 Vue 3 SFC 组件脚手架,含单元测试和类型安全 Props。当需要新建组件、拆分子组件或创建复合组件时使用。管理端用 Element Plus,用户端用 Headless UI。 |
|
⚠️ 核心执行流程已在
.cursor/rules/skill-component-scaffold.mdc中由 Cursor 自动注入。 本文件提供完整模板、代码示例和边缘场景处理,供 Agent 按需深入 Read。
Vue 3 Component Scaffold
⚠️ 前端识别:生成组件前必须确认目标前端。
- 管理端 (
Case-Database-Frontend-admin/):使用 Element Plus + Tailwind- 用户端 (
Case-Database-Frontend-user/):使用 Headless UI + Tailwind,禁止 Element Plus
触发条件
用户要求创建新的 Vue 组件、页面组件、UI 元素或交互模块。
执行流程
0. 加载规范(⚠️ 必须最先执行)
依次读取 .cursor/rules/010-typescript.mdc、.cursor/rules/011-vue.mdc、.cursor/rules/019-modular.mdc,提取类型注解要求(隐式 any 禁令、ref 泛型规范、Composable 类型规范)、script setup、defineProps/Emits、组件分类、拆分阈值、Composable 提取规则。
Tier 3:Vue 3 完整 API 见
references/vue-api-reference.md。
0.5 ⚠️ 生成前强制结构规划(禁止跳过)
写代码前必须先输出文件结构和组件职责说明。
A. 检查是否已有可复用的基础组件
在生成新组件前,先扫描以下路径,避免重复造轮子:
src/components/core/— 通用基础组件(按钮、输入框、卡片等)src/components/custom/— 业务定制组件
若已有 FormInput.vue、BaseCard.vue 等,直接复用,不重新生成。
B. 用户端表单输入:优先复用 FormInput 模式
用户端页面中表单输入框若出现 ≥3 个,必须使用 FormInput 基础组件:
<!-- src/components/core/FormInput/FormInput.vue -->
<script setup>
defineProps({
modelValue: { type: String, default: '' },
type: { type: String, default: 'text' },
placeholder: { type: String, default: '' },
icon: { type: Object, default: null }, // Lucide 图标组件
disabled: { type: Boolean, default: false },
error: { type: String, default: '' },
})
defineEmits(['update:modelValue'])
</script>
<template>
<div class="relative">
<component :is="icon" v-if="icon"
class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 dark:text-gray-500 w-5 h-5" />
<input
:type="type"
:value="modelValue"
:placeholder="placeholder"
:disabled="disabled"
@input="$emit('update:modelValue', $event.target.value)"
:class="[
'w-full border-b py-3 outline-none transition-colors',
icon ? 'pl-10 pr-4' : 'px-4',
'bg-transparent border-gray-300 dark:border-[#333333]',
'text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-600',
'focus:border-[#C41E3A] dark:focus:border-[#C41E3A]',
error ? 'border-red-500' : '',
]"
data-testid="form-input"
/>
<p v-if="error" class="mt-1 text-xs text-red-500">{{ error }}</p>
</div>
</template>
若项目中尚无
FormInput,在生成使用它的页面前先生成该核心组件。
C. 重复 UI 模式检测
检查需求中是否存在重复结构:
| 场景 | 检测标准 | 操作 |
|---|---|---|
| 表单输入项 | ≥3 个相同结构的 input 组 | 提取 FormInput.vue |
| 内容卡片 | ≥3 个相同布局的卡片 | 提取 CaseCard.vue / DesignerCard.vue |
| 操作按钮组 | ≥2 处相同按钮组合 | 提取 ActionGroup.vue |
| 阶段/Tab 面板 | ≥2 个结构相同的面板 | 提取 StagePanel.vue |
D. 输出组件文件结构(代码前必须输出)
# 本次将生成以下文件:
src/components/<type>/<ComponentName>/
├── <ComponentName>.vue ← 主组件(目标 ≤ 120 行)
├── <ComponentName>.test.ts ← 单元测试
└── index.ts ← barrel export
# 依赖(已存在 / 需新建):
- FormInput.vue: [已存在 / 需新建]
- BaseCard.vue: [已存在 / 不需要]
1. 确认组件规格
| 字段 | 必填 | 默认值 |
|---|---|---|
| 组件名 | ✅ | — |
| 组件类型 | ❌ | UI 组件 |
| 所在目录 | ❌ | src/components/ |
| Props / Emits | ❌ | 根据需求推断 |
类型与目录:core→src/components/core/、custom→custom/、layout→layouts/、page→views/<module>/components/、form→custom/。
2. 扫描项目模式
读取 src/components/ 确认 Props 声明、样式方案、命名前缀约定、已有的 core 组件列表。
3. 生成文件结构
按步骤 0.5 输出的结构逐文件生成,禁止将多个组件合并到一个文件。
src/components/<type>/<ComponentName>/
├── <ComponentName>.vue
├── <ComponentName>.test.ts
└── index.ts
4. 组件模板
根据类型选择:基础 UI / 表格 / 表单对话框 / 复合组件。完整模板与设计决策树见 Tier 3。
单组件行数限制:
- template 区域 ≤ 80 行
- script 区域 ≤ 60 行(超出提取 composable)
- 整个 SFC ≤ 150 行(超出必须拆分子组件)
4.5 设计决策树(必须执行)
- ≥3 布尔 prop?→ 显式变体组件
- 多子组件共享状态?→ provide/inject
- script 逻辑 > 60 行?→ 提取
use<ComponentName>.tscomposable(必须遵循010-typescript.mdc的 Composable 类型规范:参数有类型、ref有泛型、业务类型用import type) -
2 处复用?→
src/components/+ slot - 同一 UI 结构重复 ≥3 次?→ 提取基础组件(见步骤 0.5C)
- 否则 → 基础 UI 模板
5. 测试与 Barrel export
测试至少包含渲染测试。index.ts 导出:export { default as ComponentName } from './ComponentName.vue'。详细测试见 vue-testing 技能。
验证
- ESLint 无报错
- Props 使用对象语法
defineProps({ key: { type, default } }),若模板可直接访问 props 字段则不保存返回值(避免unused-vars报错;仅当 script 中需要访问props.xxx时才保存:const props = defineProps(...)) - Emits 使用数组语法,事件名使用 camelCase(非 kebab-case)
- 包含
data-testid - 测试至少一个渲染测试
- 管理端:Element Plus 用于组件,Tailwind 用于布局;用户端:Headless UI + Tailwind(禁止 Element Plus)
- barrel export 正确
- 单 SFC ≤ 150 行
- 重复 UI 结构(≥3 次)已提取为基础组件
- 表单输入 ≥3 个已复用 FormInput.vue(用户端)
- 子组件模板中无
v-model直接绑定 prop 嵌套属性(参见011-vue.mdcProps 数据流模式) - 提取的 composable(use.ts)参数有类型注解、
ref([])/ref(null)有泛型标注*(参见010-typescript.mdcComposable 类型规范)
Red Flags(触发则停止并重构)
- ❌ 未输出文件结构直接写代码 → 停止,先输出步骤 0.5D 的结构
- ❌ 单 SFC > 150 行 → 拆分子组件
- ❌ 相同 Tailwind 输入框结构写了 ≥3 次 → 提取 FormInput.vue
- ❌ Options API → 改用
<script setup> - ❌
const props = defineProps(...)但 script 中从不访问props.xxx→ 去掉const props =,避免unused-varslint 报错 - ❌
catch (e) { ... }但 catch 块中不使用e→ 改为catch { ... }(无参 catch),避免unused-vars报错 - ❌ 直接修改 props → 通过 emit(含隐蔽变体:
v-model="prop.field"、v-model="prop[key]"、事件回调中prop[k] = v) - ❌ watch deep:true 滥用 → 精准监听
- ❌ script 逻辑 > 60 行未提取 → 拆分为 use*.ts
- ❌ composable 参数无类型 /
ref([])无泛型 → 补全类型注解(strict: true下必报错) - ❌ ≥3 布尔 prop 控制渲染 → 拆分变体
- ❌ prop drilling 传递同状态 → 改用 provide/inject
Tier 3 深度参考
| 文件 | 内容 |
|---|---|
references/component-templates.md |
基础/表格/表单/复合组件模板、决策树、Prop 反模式、错误处理、UI 反模式 |
references/vue-api-reference.md |
Vue 3 完整 API 索引 |
references/naming-conventions.md |
组件命名规范 |