Initial commit
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
---
|
||||
name: vue2-element-dev
|
||||
description: Use when implementing or modifying Vue2 + Element UI pages/components in this repository, especially for list pages, form dialogs, API integration, route/menu wiring, and pre-delivery checks.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Vue2 Element Dev
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 新增或修改 `src/views` 下的页面(列表、表单、弹窗)
|
||||
- 新增或修改 `src/components` 下的复用组件
|
||||
- 做接口联调、参数映射、提交与回显
|
||||
- 做路由接入、导航菜单接入、权限相关入口调整
|
||||
|
||||
## 项目默认约定(平衡型)
|
||||
|
||||
- **优先复用现有模式**:先参考同目录页面写法,再补差异
|
||||
- **命名可读**:页面目录、组件名、方法名优先语义化
|
||||
- **数据分层**:查询参数、表单数据、UI 状态分开管理
|
||||
- **接口调用单点化**:统一放到 `src/api/*.js`,页面仅做调用和状态处理
|
||||
- **样式就近维护**:页面样式写在组件内,避免全局污染
|
||||
|
||||
## 页面开发流程
|
||||
|
||||
1. 先找一个同类型页面作为参考(表格页/配置页/弹窗页)
|
||||
2. 搭出页面骨架:筛选区、操作区、表格区、分页区
|
||||
3. 补齐数据结构:`list`、`loading`、`query`、`pagination`
|
||||
4. 接入接口:查询、重置、增删改、状态切换
|
||||
5. 完成交互反馈:确认弹窗、成功提示、失败兜底
|
||||
6. 执行构建或 lint 验证,修复明显问题后再交付
|
||||
|
||||
## 推荐代码组织
|
||||
|
||||
- `data`
|
||||
- 查询模型:`queryForm`
|
||||
- 列表模型:`tableData`
|
||||
- 状态模型:`loading` / `dialogVisible` / `submitLoading`
|
||||
- `computed`
|
||||
- 放衍生值,不放有副作用逻辑
|
||||
- `methods`
|
||||
- `fetchList`:统一列表查询入口
|
||||
- `handleSearch` / `handleReset`
|
||||
- `handleAdd` / `handleEdit` / `handleDelete`
|
||||
- `handleSubmit`:表单提交统一出口
|
||||
|
||||
## 接口联调约定
|
||||
|
||||
- API 文件里维护请求函数,页面中只做调用
|
||||
- 参数透传前做必要整形(时间范围、数字布尔转换)
|
||||
- 提交前做前端必填校验;提交后统一提示
|
||||
- 失败场景要收敛:关闭 loading、保留用户输入、提示可理解信息
|
||||
|
||||
## 路由与菜单接入约定
|
||||
|
||||
- 新页面需要补路由入口与导航入口
|
||||
- 菜单文案与页面标题保持一致,避免多处命名不一致
|
||||
- 权限字段沿用现有规则,不自创新字段名
|
||||
|
||||
## 交付前最低检查
|
||||
|
||||
- 页面可正常进入,主流程可跑通(查、增/改、删)
|
||||
- 列表页分页/筛选联动正常
|
||||
- 弹窗表单可回显、可校验、可提交
|
||||
- 无明显控制台报错,构建或 lint 不出现新增阻断错误
|
||||
|
||||
## 快速参考
|
||||
|
||||
- 详细示例:见 [examples.md](examples.md)
|
||||
- 提交前清单:见 [checklist.md](checklist.md)
|
||||
@@ -0,0 +1,25 @@
|
||||
# Checklist
|
||||
|
||||
## 开发中检查
|
||||
|
||||
- [ ] 页面结构完整(筛选、表格、分页/操作区)
|
||||
- [ ] `loading` 状态在请求前后正确切换
|
||||
- [ ] 查询、重置、分页逻辑一致
|
||||
- [ ] 新增/编辑弹窗支持回显、校验、提交
|
||||
- [ ] 删除/状态切换有确认提示与成功反馈
|
||||
- [ ] API 参数字段与后端约定一致
|
||||
|
||||
## 接入检查
|
||||
|
||||
- [ ] 新页面路由已添加
|
||||
- [ ] 导航菜单已添加或已挂接
|
||||
- [ ] 页面标题、菜单名称、权限标识一致
|
||||
- [ ] 未破坏已有页面入口
|
||||
|
||||
## 交付前检查
|
||||
|
||||
- [ ] 至少执行一次 `npm run lint` 或 `npm run test-build`
|
||||
- [ ] 控制台无新增阻断错误
|
||||
- [ ] 主流程可复现(查、增/改、删)
|
||||
- [ ] 异常分支有兜底(失败提示、loading 关闭)
|
||||
- [ ] 仅提交与本需求相关改动
|
||||
@@ -0,0 +1,127 @@
|
||||
# Examples
|
||||
|
||||
## 列表页最小骨架(Vue2 + Element UI)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="page-wrap">
|
||||
<el-form :inline="true" :model="queryForm">
|
||||
<el-form-item label="关键词">
|
||||
<el-input v-model="queryForm.keyword" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="tableData" v-loading="loading" border>
|
||||
<el-table-column prop="id" label="ID" width="100" />
|
||||
<el-table-column prop="name" label="名称" min-width="160" />
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
:current-page="queryForm.pageNum"
|
||||
:page-size="queryForm.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getList } from "@/api/demo";
|
||||
|
||||
export default {
|
||||
name: "DemoList",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
total: 0,
|
||||
queryForm: {
|
||||
keyword: "",
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fetchList();
|
||||
},
|
||||
methods: {
|
||||
async fetchList() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await getList(this.queryForm);
|
||||
this.tableData = res?.data?.list || [];
|
||||
this.total = res?.data?.total || 0;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
handleSearch() {
|
||||
this.queryForm.pageNum = 1;
|
||||
this.fetchList();
|
||||
},
|
||||
handleReset() {
|
||||
this.queryForm.keyword = "";
|
||||
this.queryForm.pageNum = 1;
|
||||
this.fetchList();
|
||||
},
|
||||
handlePageChange(page) {
|
||||
this.queryForm.pageNum = page;
|
||||
this.fetchList();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
## API 文件示例
|
||||
|
||||
```js
|
||||
import request from "@/utils/request";
|
||||
|
||||
export function getList(params) {
|
||||
return request({
|
||||
url: "/api/demo/list",
|
||||
method: "get",
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
export function createItem(data) {
|
||||
return request({
|
||||
url: "/api/demo/create",
|
||||
method: "post",
|
||||
data
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## 表单弹窗处理示例
|
||||
|
||||
```js
|
||||
methods: {
|
||||
openDialog(row) {
|
||||
this.dialogVisible = true;
|
||||
this.formData = row
|
||||
? { id: row.id, name: row.name }
|
||||
: { id: "", name: "" };
|
||||
},
|
||||
async handleSubmit() {
|
||||
this.submitLoading = true;
|
||||
try {
|
||||
await this.$refs.formRef.validate();
|
||||
await createItem(this.formData);
|
||||
this.$message.success("保存成功");
|
||||
this.dialogVisible = false;
|
||||
this.fetchList();
|
||||
} finally {
|
||||
this.submitLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user