110 lines
4.1 KiB
Markdown
110 lines
4.1 KiB
Markdown
# 3.10 前端联调指引
|
||
|
||
:::tip 概述
|
||
后端四件套就绪后,前端补两块:API 模块(`ui/src/api`)与页面(`ui/src/views`)。本页以 example 模块的真实前端文件为准,深入开发规范见 [第 9 章 前端开发](/zh/frontend/overview.html)。
|
||
:::
|
||
|
||
## 涉及文件(example 模块实际结构)
|
||
|
||
```text
|
||
ui/src/api/tool/example.ts # API 模块(九个接口函数)
|
||
ui/src/views/tool/example/
|
||
├── index.vue # 列表页(BasicTable + 查询表单 + 工具栏按钮)
|
||
├── edit.vue # 新增/编辑弹窗(主键为 0 时表示新增)
|
||
├── detail.vue # 详情弹窗
|
||
├── columns.ts # 表格列配置(render 函数自定义图片/标签渲染)
|
||
└── querySchemas.ts # 查询表单配置(FormSchema 声明式)
|
||
```
|
||
|
||
## API 模块
|
||
|
||
```typescript
|
||
// ui/src/api/tool/example.ts(节选,完整内容见源文件)
|
||
import { http } from '@/utils/http/axios';
|
||
|
||
export function getExamplePage(params?: Record<string, any>) {
|
||
return http.request({ url: '/example/page', method: 'GET', params });
|
||
}
|
||
|
||
export function exampleAdd(data: any) {
|
||
return http.request({ url: '/example/add', method: 'POST', data });
|
||
}
|
||
|
||
export function exampleDelete(id) {
|
||
return http.request({ url: '/example/delete/' + id, method: 'DELETE' });
|
||
}
|
||
```
|
||
|
||
约定:URL 不带 `/api` 前缀(`VITE_GLOB_API_URL_PREFIX` 统一拼接);Token 由请求拦截器自动携带;响应信封由拦截器解包,业务层直接拿 data。
|
||
|
||
## 页面结构
|
||
|
||
列表页(index.vue)骨架摘自 `ui/src/views/tool/example/index.vue`:
|
||
|
||
```html
|
||
<PageWrapper>
|
||
<!-- 查询表单卡片(querySchemas.ts 声明驱动) -->
|
||
<el-card :bordered="false" class="pt-3 mb-3 proCard">
|
||
<BasicForm @register="register" @submit="handleSubmit" @reset="handleReset" />
|
||
</el-card>
|
||
|
||
<!-- 数据表格卡片(columns.ts 列配置 + 分页请求) -->
|
||
<el-card :bordered="false" class="proCard">
|
||
<BasicTable
|
||
:columns="columns"
|
||
:request="loadDataTable"
|
||
:row-key="(row) => row.id"
|
||
:actionColumn="actionColumn"
|
||
@selection-change="onSelectionChange"
|
||
>
|
||
<template #tableTitle>
|
||
<el-button type="primary" @click="handleAdd" v-perm="['sys:example:add']">添加案例演示</el-button>
|
||
</template>
|
||
</BasicTable>
|
||
</el-card>
|
||
</PageWrapper>
|
||
```
|
||
|
||
模式要点:`BasicForm`/`BasicTable` 经 `@register` 注册使用;表格数据由 `request` 回调返回(页面里即 `getExamplePage`);工具栏按钮挂 `v-perm` 数组入参。
|
||
|
||
## 列配置与查询配置
|
||
|
||
```typescript
|
||
// ui/src/views/tool/example/columns.ts(节选)
|
||
export const columns = [
|
||
{ type: 'selection' },
|
||
{ label: '主键ID', prop: 'id', minWidth: 100 },
|
||
{ label: '案例名称', prop: 'name', minWidth: 120 },
|
||
{ label: '案例图片', prop: 'avatar', minWidth: 120,
|
||
render(record) { return h(ElAvatar, { size: 48 /* ... */ }); } },
|
||
// ...
|
||
];
|
||
|
||
// ui/src/views/tool/example/querySchemas.ts(节选)
|
||
import { FormSchema } from '@/components/Form/index';
|
||
export const schemas: FormSchema[] = [
|
||
{ field: 'name', component: 'Input', label: '案例名称',
|
||
componentProps: { placeholder: '请输入案例名称', clearable: true } },
|
||
// 类型/状态用 Select(后端 EqFields 过滤)
|
||
];
|
||
```
|
||
|
||
字典列(statusText)直接配置为普通列显示——显示名已由后端 SerializeMaps 回填。
|
||
|
||
## 联调检查清单
|
||
|
||
- [ ] 菜单已配置且 `component` 路径指向新页面(否则路由 404)
|
||
- [ ] 角色已授权页面与按钮权限节点
|
||
- [ ] API 模块函数与 RouteSpec 动作一一对应
|
||
- [ ] 表单字段与 dto json 名一致(全量提交语义)
|
||
- [ ] 导入走 FormData(`UnmarshalBody` 契约,页面经 importFile 组件),导出拿 URL 下载
|
||
- [ ] 字典下拉走字典接口取值,不硬编码枚举
|
||
|
||
## 用生成器起步
|
||
|
||
`make gen` 会同时产出 API 模块与上述五个页面文件(树形表走 ui2 模板),生成后按业务微调即可。生成细节见 [第 10 章 代码生成器](/zh/generator/overview.html)。
|
||
|
||
## 下一步
|
||
|
||
→ [3.11 完整代码清单](/zh/guide/code-list.html)
|