签入版本
This commit is contained in:
@@ -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)。
|
||||
|
||||
Reference in New Issue
Block a user