签入版本

This commit is contained in:
2026-10-08 17:50:29 +08:00
parent 809750b47e
commit e213a9b31d
444 changed files with 39366 additions and 37533 deletions
@@ -13,10 +13,14 @@
4. 创建列表页 src/views/{module}/{name}/index.vue
5. 创建编辑弹窗 src/views/{module}/{name}/edit.vue
6. 创建详情弹窗 src/views/{module}/{name}/detail.vue
7. 配置菜单 后端菜单管理中设置组件路径
7. 配置菜单 后端菜单管理中设置组件路径 + 权限节点
8. 测试 刷新前端页面,点击菜单测试
```
:::tip 更快的方式
以上 6 个前端文件可由后端**代码生成器**一键生成(`POST /api/generator/generate`),生成后从第 7 步开始即可。手动编写适合需要深度定制的页面。见 [第 10 章 代码生成器](/zh/generator/overview.html)。
:::
## 完整文件结构
```text
@@ -70,7 +74,7 @@ export function exampleDelete(id) {
return http.request({ url: '/example/delete/' + id, method: 'DELETE' });
}
// 批量删除
// 批量删除(本项目为 DELETE,请求体 { ids: [] })
export function exampleBatchDelete(data: any) {
return http.request({ url: '/example/batchDelete', method: 'DELETE', data });
}
@@ -86,7 +90,7 @@ export function exampleBatchDelete(data: any) {
| 新增 | `{module}Add` | POST |
| 修改 | `{module}Update` | PUT |
| 删除 | `{module}Delete` | DELETE |
| 批量删除 | `{module}BatchDelete` | DELETE |
| 批量删除 | `{module}BatchDelete` | **DELETE**(带 body) |
## 步骤二:创建表格列配置
@@ -117,8 +121,8 @@ export const columns = [
// 选项标签渲染:按值映射文字与颜色
render(record) {
const statusMap = {
0: { text: '禁用', type: 'danger' },
1: { text: '启用', type: 'success' },
2: { text: '停用', type: 'danger' },
1: { text: '在用', type: 'success' },
};
const status = statusMap[record.row.status];
return h(ElTag, { type: status?.type || 'info' }, {
@@ -159,12 +163,16 @@ export const columns = [
|------|------|------|
| `type` | string | 列类型:`selection`(多选框)/ `index`(序号) |
| `label` | string | 表头标题 |
| `prop` | string | 数据字段名(camelCase) |
| `prop` | string | 数据字段名(camelCase,与后端响应一致) |
| `fixed` | string | 固定列:`left` / `right` |
| `width` | number | 固定宽度 |
| `minWidth` | number | 最小宽度(自适应) |
| `render` | function | 自定义渲染函数 |
:::tip 状态值对齐
`statusMap` 的值域必须与数据库/字典一致(如 example:1=在用 2=停用)。推荐直接使用后端补全的 `statusText` 字段,仅颜色按值映射。
:::
## 步骤三:创建查询表单配置
```typescript
@@ -189,8 +197,8 @@ export const schemas: FormSchema[] = [
placeholder: '请选择状态',
clearable: true,
options: [
{ label: '启用', value: 1 },
{ label: '禁用', value: 0 },
{ label: '在用', value: 1 },
{ label: '停用', value: 2 },
],
},
},
@@ -206,6 +214,10 @@ export const schemas: FormSchema[] = [
| `InputNumber` | 数字字段 | 数字输入框 |
| `DatePicker` | 日期字段 | 日期选择器 |
:::tip 查询字段须与后端配置对应
`field` 必须是后端 Service `pageLikeFields`(模糊)或 `pageEqFields`(精确)中声明的字段,否则查询条件不生效(见 [8.3 分页查询规范](/zh/api-spec/pagination.html))。下拉选项也可用 `getDictItemByCode(code)` 从字典动态加载。
:::
## 步骤四:创建列表页
```vue
@@ -218,10 +230,10 @@ export const schemas: FormSchema[] = [
<!-- 操作栏 -->
<el-row :gutter="10" style="margin-bottom: 10px;">
<el-col :span="1.5">
<el-button type="primary" @click="handleAdd">新增</el-button>
<el-button v-perm="['sys:example:add']" type="primary" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" :disabled="!selectedIds.length" @click="handleBatchDelete">
<el-button v-perm="['sys:example:batchDelete']" type="danger" :disabled="!selectedIds.length" @click="handleBatchDelete">
批量删除
</el-button>
</el-col>
@@ -236,9 +248,9 @@ export const schemas: FormSchema[] = [
</el-table-column>
<el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button type="primary" link @click="handleDetail(row)">详情</el-button>
<el-button type="danger" link @click="handleDelete(row.id)">删除</el-button>
<el-button v-perm="['sys:example:update']" type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button v-perm="['sys:example:detail']" type="primary" link @click="handleDetail(row)">详情</el-button>
<el-button v-perm="['sys:example:delete']" type="danger" link @click="handleDelete(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -280,7 +292,7 @@ const selectedIds = ref<number[]>([]);
const detailVisible = ref(false);
const detailId = ref(0);
// 加载数据
// 加载数据(分页结构:records / total / current / size / pages)
const fetchData = async () => {
loading.value = true;
try {
@@ -331,7 +343,7 @@ const handleDelete = async (id) => {
fetchData();
};
// 批量删除
// 批量删除(DELETE,请求体 { ids: [...] })
const handleBatchDelete = async () => {
await ElMessageBox.confirm(`确认删除选中的 ${selectedIds.value.length} 条记录?`, '提示', { type: 'warning' });
await exampleBatchDelete({ ids: selectedIds.value });
@@ -365,8 +377,8 @@ onMounted(() => fetchData());
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :value="1">启用</el-radio>
<el-radio :value="0">禁用</el-radio>
<el-radio :value="1">在用</el-radio>
<el-radio :value="2">停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序" prop="sort">
@@ -382,7 +394,7 @@ onMounted(() => fetchData());
<script setup lang="ts">
import { exampleAdd, exampleUpdate, getExampleDetail } from '@/api/tool/example';
import { getDictItemList } from '@/api/common/index';
import { getDictItemByCode } from '@/api/common/index';
import { ElMessage } from 'element-plus';
const emit = defineEmits(['success']);
@@ -410,8 +422,8 @@ const open = async (row?) => {
visible.value = true;
formData.value = { id: 0, name: '', type: '', status: 1, sort: 0 };
// 加载字典选项
typeOptions.value = await getDictItemList('example_type');
// 加载字典选项(与后端字典编码一致)
typeOptions.value = await getDictItemByCode('example_type');
// 编辑时加载详情
if (row?.id) {
@@ -443,6 +455,10 @@ defineExpose({ open });
</script>
```
:::tip 表单规则与后端对齐
前端 `rules`(必填/长度/枚举)应与后端 `ExampleRequest::rules()` 一致——前端是体验提示,后端是最终防线(见 [3.4 FormRequest 验证器](/zh/guide/validator.html))。
:::
## 步骤六:创建详情弹窗
```vue
@@ -509,7 +525,7 @@ const formData = reactive({
createTime: '',
});
// 加载详情
// 加载详情(typeText/statusText 由后端 serializeMaps 补全)
const loadDetail = async () => {
try {
const data = await getExampleDetail(props.id);
@@ -541,32 +557,37 @@ watch(() => props.visible, (newVal) => {
| 菜单名称 | 案例管理 |
| 组件路径 | `tool/example/index` |
| 路由路径 | `/tool/example` |
| 权限标识 | `sys:example:page` |
| 菜单类型 | 菜单/页面(type=0) |
添加按钮权限:
添加权限节点(type=1,参与接口鉴权):
| 按钮名称 | 权限标识 |
| 节点名称 | 权限标识 |
|---------|---------|
| 查看 | `sys:example:page` |
| 新增 | `sys:example:add` |
| 修改 | `sys:example:update` |
| 删除 | `sys:example:delete` |
| 批量删除 | `sys:example:batchDelete` |
| 详情 | `sys:example:detail` |
(详见 [3.8 权限节点配置](/zh/guide/permission.html),菜单类型语义见 [6.3 菜单管理](/zh/modules/menu.html)。)
## 步骤八:测试
1. 刷新前端页面
2. 点击菜单"案例管理"
3. 测试搜索、新增、编辑、详情、删除、批量删除功能
1. 为测试角色分配新菜单权限
2. 刷新前端页面(或重新登录使动态路由生效)
3. 点击菜单"案例管理"
4. 测试搜索、新增、编辑、详情、删除、批量删除功能
5. 用无权限账号登录,确认 `v-perm` 按钮隐藏、直接调接口返回 403
## 代码生成器(快速创建)
如果模块结构简单,可使用代码生成器一键生成前后端代码:
1. 建好数据库表
2. 访问 `/api/generator/page` 查看可生成的表
3. `POST /api/generator/preview` 预览代码
4. `POST /api/generator/generate` 生成代码
2. `GET /api/generator/page` 查看可生成的表
3. `GET /api/generator/preview` 预览代码
4. `POST /api/generator/generate` 生成代码(`batchGenerate` 批量)
5. 在菜单管理中配置菜单
详见[代码生成器](/zh/generator/overview.html)。
详见 [第 10 章 代码生成器](/zh/generator/overview.html)。