init: 初始化 hgmj 后台管理项目

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 17:48:09 +08:00
co-authored by Claude Opus 4.7
commit 397398b746
645 changed files with 179153 additions and 0 deletions
+70
View File
@@ -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 关闭)
- [ ] 仅提交与本需求相关改动
+127
View File
@@ -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;
}
}
}
```