初始化

This commit is contained in:
2026-03-05 21:27:11 +08:00
commit 130de0fd5d
140 changed files with 21972 additions and 0 deletions

200
.cursor/rules/011-vue.mdc Normal file
View File

@@ -0,0 +1,200 @@
---
description: "Vue 3 + Vite 前端开发规范TypeScript。管理端使用 Element Plus用户端使用 Headless UI + Tailwind禁止 Element Plus"
globs:
- "**/*.vue"
- "Case-Database-Frontend-user/src/**/*.ts"
- "Case-Database-Frontend-admin/src/**/*.ts"
alwaysApply: false
---
# Vue 3 / Vite Standards + 双前端上下文
> **双前端区分**:管理端 (`Case-Database-Frontend-admin/`) 使用 Element Plus
> 用户端 (`Case-Database-Frontend-user/`) 使用 Headless UI + Tailwind CSS**禁止引入 Element Plus**。
## 组件与脚本约束
- 默认使用 `<script setup>` + Composition API
- Props/Emits 显式声明,避免隐式双向修改
- 页面与组件职责分离:页面编排,组件渲染
- 可复用逻辑抽离到 composables / utils
### Emits 命名约定
- `defineEmits` 中自定义事件使用 **camelCase**`addItem`、`removeItem`
- `update:xxx` 前缀保留给 v-model 双向绑定约定
- 模板中 `@add-item` 和 `@addItem` 均可Vue 自动转换),但 defineEmits 源头必须 camelCase
```vue
<!-- ❌ kebab-case -->
defineEmits<{ 'add-item': []; 'remove-item': [id: number] }>()
<!-- ✅ camelCase -->
defineEmits<{ addItem: []; removeItem: [id: number] }>()
```
## 状态与数据流
- 全局状态使用 Pinia局部状态留在组件内
- 单向数据流:`props down, events up`
- 远端请求统一经 service/api 层,不在模板内混写
### Props 数据流模式(⚠️ 必须遵守)
子组件**禁止**直接修改 prop 或 prop 的嵌套属性。变更必须通过 emit 通知父组件。
**模式 A子组件接收对象 prop需修改其字段**
```vue
<!-- ❌ 直接 v-model 绑定 prop 字段(触发 vue/no-mutating-props -->
<AppInput v-model="form.username" />
<!-- ✅ 拆分为 :model-value + emit -->
<AppInput
:model-value="form.username"
@update:model-value="emit('update:form', { ...props.form, username: $event })"
/>
```
**模式 B子组件中继 v-model 到子子组件**
```vue
<!-- ❌ 用 v-model 直接绑定动态 prop key -->
<RadioGroup v-model="radios[f.key]" />
<!-- ✅ 拆分并向上 emit -->
<RadioGroup
:model-value="radios[f.key]"
@update:model-value="emit('update:radio', f.key, $event)"
/>
```
**模式 C事件回调中修改 prop隐蔽变体**
```vue
<!-- ❌ 在事件处理中直接赋值 prop 属性 -->
<LogicTable @update="(k, v) => logics[k] = v" />
<!-- ✅ 转发为 emit -->
<LogicTable @update="(k, v) => emit('update:logic', k, v)" />
```
**父组件响应模式(状态持有方):**
```vue
<!-- 父组件持有 reactive 状态,处理 update 事件 -->
<ChildForm
:form="formData"
@update:form="Object.assign(formData, $event)"
/>
<!-- 或逐字段更新 -->
<FilterBody
:radios="radios"
@update:radio="(key, val) => radios[key] = val"
/>
```
## 性能与可维护性
- 列表必须 `key` 稳定;重计算走 `computed`
- 禁止在模板调用高开销函数
### SFC 行数限制(强制)
- template <= 80 行script <= 60 行,整个 SFC <= 150 行
- 超出 script 60 行 → 提取 composable (`use<Name>.ts`)
- 超出 SFC 150 行 → 拆分子组件
### 组件设计决策(新建和修改时均适用)
- >=3 个布尔 prop → 拆分为显式变体组件
- 多子组件共享状态 → provide/inject
- script > 60 行 → 提取 `use<Name>.ts` composable
- 同一 UI 结构出现 >=3 次 → 提取基础组件
## 可访问性与一致性
- 表单项必须有可读 label / 提示
- 错误状态、空状态、加载状态必须可见
- 命名、目录、导出方式保持一致
## 验证清单
- [ ] ESLint 无错误
- [ ] 子组件不直接修改 prop无 `v-model="prop.field"` 或 `prop[key] = val` 模式)
- [ ] defineEmits 使用 camelCase 命名(非 kebab-case
- [ ] 关键交互具备加载/错误处理
- [ ] 组件结构清晰,可复用逻辑已抽离
- [ ] UI 语义与可访问性基础达标
---
## 双前端上下文规则
Agent 在生成代码前必须先判断目标前端:
- 路径含 `Case-Database-Frontend-user/` → **用户端模式**
- 路径含 `Case-Database-Frontend-admin/` → **管理端模式**
- 路径含 `Case-Database-Frontend-shared/` → **共享层模式**(纯 JS无 UI 依赖)
### 用户端规则
- **移动端优先**:断点顺序 `默认(xs) → sm → md → lg → xl`
- 触摸目标最小 44x44px交互元素间距 >= 8px
- UI 组件使用 Tailwind CSS + 自定义组件(非 Element Plus
- 图片必须指定 `width`、`height`、`alt`,推荐 WebP
- 路由懒加载:每个页面独立 chunk
- 禁止在用户端引入 Element Plus
- 权限模型:登录态 + 会员等级
- API 基础路径:`/api/`
- 首屏 LCP 目标 <= 2.5s(移动网络),单路由 chunk <= 200KB
### 管理端规则
- **桌面端优先**:以 1280px 宽度为基准,支持 >= 1024px
- 优先使用 Element Plus 标准组件(`el-table`、`el-form`、`el-dialog`
- 每个操作必须有权限检查(`v-permission`
- 危险操作必须有二次确认(`el-popconfirm`
- 表单使用 `el-form` + `:rules` 验证,弹窗关闭时重置
- 分页统一 `el-pagination`,默认 page_size: 10
- 表格操作列固定右侧(`fixed="right"`
- API 基础路径:`/admin/`
- 401 → 清除 Token → 登录页403 → Toast "无权限"
### 共享层规则
- 只允许纯 TypeScript禁止引入 Vue、Element Plus、axios
- 导出的函数/常量必须同时适配用户端和管理端
- 修改后需同步通知两个前端的相关使用方
## ESLint 配置注意事项
### vue/block-order 等 Vue 专属规则必须限定文件范围
`vue/block-order` 规则依赖 Vue 文件解析器,**不能**放在全局 `rules` 中,否则对 `.md`、`.json`、`.ts` 等非 Vue 文件生效时会崩溃:
```js
// ❌ 错误全局规则对所有文件生效README.md 会 TypeError 崩溃
export default antfu({
rules: {
'vue/block-order': ['error', { order: ['script', 'template', 'style'] }],
},
});
// ✅ 正确:限定在 *.vue 文件
export default antfu({ ... }, {
files: ['**/*.vue'],
rules: {
'vue/block-order': ['error', { order: ['script', 'template', 'style'] }],
},
});
```
所有 `vue/*` 前缀的规则若要手动覆盖,都必须放在 `files: ['**/*.vue']` 覆盖块内。
## Tier 3 深度参考
- `.cursor/rules/references/011-vue-deep.md` — 完整 Vue 规范与反模式示例
- `docs/architecture/frontend-strategy.md` — 完整双前端架构说明
- `docs/guides/ui-specs-user.md` — 用户端 UI 规范
- `docs/guides/ui-specs-admin.md` — 管理端 UI 规范