# 前端页面视图
前端页面视图是用户直接交互的界面。项目使用 Vue3 + AntDesign Vue + Vite 构建。一个完整的 CRUD 页面由 4 个文件组成:
```
ui/src/views/system/position/
├── index.vue # 主页面:搜索 + 表格 + 操作
├── edit.vue # 编辑弹窗:新增/编辑表单
├── columns.ts # 表格列定义
└── querySchemas.ts # 搜索表单 Schema
```
## 1. 主页面 `index.vue`
主页面是模块的入口页面,包含搜索表单、数据表格和操作按钮。
### 完整代码
```vue
新建
删除
编辑
删除
```
### 代码解析
**页面结构**:`PageWrapper` 包裹两个 `a-card`,上方是搜索表单,下方是数据表格。
**搜索表单**:通过 `useForm` 注册,Schema 从 `querySchemas.ts` 导入。
**数据表格**:通过 `BasicTable` 组件渲染,`request` 属性绑定数据加载函数,`columns` 从 `columns.ts` 导入。
**操作列**:通过 `bodyCell` 插槽实现,配合 `v-perm` 指令控制权限。
**编辑弹窗**:使用 `defineAsyncComponent` 懒加载 `edit.vue`,通过 `v-model:visible` 控制显示,`positionId` 传入编辑记录 ID(0 表示新增)。
**删除逻辑**:使用 `Modal.confirm` 确认弹窗,单条删除传入 `id`,批量删除从 `selectionData` 取已选 ID。
## 2. 编辑弹窗 `edit.vue`
编辑弹窗是独立组件,负责新增和编辑操作的表单展示与提交。
### 完整代码
```vue
正常
停用
```
### 代码解析
**Props 接收**:
- `visible`:控制弹窗显示/隐藏,通过 `v-model:visible` 双向绑定
- `positionId`:编辑记录的 ID,为 0 时表示新增模式
**表单数据**:使用 `reactive` 定义,字段与后端 Schema 对应。
**新增/编辑判断**:`props.positionId ? positionUpdate : positionAdd`,通过 ID 是否为 0 区分。
**防重复提交**:`useLockFn` 包装提交函数,点击后按钮变为 loading 状态,防止重复提交。
**数据回填**:`onMounted` 时判断如果是编辑模式,调用 `getPositionDetail` 获取数据并填充表单。
**事件通信**:
- `emit('update:visible', false)`:关闭弹窗
- `emit('success')`:通知父页面刷新表格
## 3. 表格列定义 `columns.ts`
定义表格的列配置,包括列名、字段名、宽度和自定义渲染。
### 完整代码
```typescript
import { h } from 'vue';
import { Tag } from 'ant-design-vue';
export const columns = [
{
title: 'ID',
dataIndex: 'id',
fixed: 'left',
width: 50,
},
{
title: '岗位名称',
dataIndex: 'name',
width: 100,
},
{
title: '岗位状态',
dataIndex: 'status',
width: 100,
customRender({ record }) {
return h(
Tag,
{ color: record.status == 1 ? 'success' : 'error' },
{ default: () => (record.status == 1 ? '正常' : '停用') },
);
},
},
{
title: '排序',
dataIndex: 'sort',
width: 100,
},
{
title: '创建人',
dataIndex: 'createUser',
width: 100,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
},
{
title: '操作',
fixed: 'right',
dataIndex: 'action',
key: 'action',
width: 200,
},
];
```
### 代码解析
**列定义**:`title` 为列标题,`dataIndex` 为字段名。
**固定列**:`fixed: 'left'` 固定在左侧,`fixed: 'right'` 固定在右侧(操作列)。
**自定义渲染**:`customRender({ record })` 使用 Vue 的 `h()` 函数创建虚拟 DOM。状态列将 `1/2` 渲染为绿色/红色标签。
**操作列**:需要设置 `key: 'action'`,配合 `bodyCell` 插槽使用。
## 4. 搜索表单 `querySchemas.ts`
定义搜索表单的字段配置,使用项目封装的 `FormSchema` 类型。
### 完整代码
```typescript
import { FormSchema } from '@/components/Form/index';
export const schemas: FormSchema[] = [
{
name: 'name',
component: 'Input',
label: '岗位名称',
componentProps: {
placeholder: '请输入岗位名称',
},
},
{
name: 'status',
component: 'Select',
label: '状态',
componentProps: {
placeholder: '请选择状态',
clearable: true,
options: [
{
label: '正常',
value: '1',
},
{
label: '禁用',
value: '2',
},
],
},
},
];
```
### 代码解析
**FormSchema 字段**:
| 字段 | 说明 |
|:----|:----|
| `name` | 字段名,与后端查询参数对应 |
| `component` | 表单组件类型:`Input` / `Select` / `DatePicker` 等 |
| `label` | 标签文本 |
| `componentProps` | 组件属性,透传给 AntDesign Vue 组件 |
**Select 组件**:`options` 定义下拉选项,`clearable: true` 允许清空选择。
## 文件关系图
```
index.vue(主页面)
├── import schemas from './querySchemas' ← 搜索表单配置
├── import columns from './columns' ← 表格列配置
├── import editDialog from './edit.vue' ← 编辑弹窗组件
└── import API from '@/api/system/position' ← 接口请求
edit.vue(编辑弹窗)
└── import API from '@/api/system/position' ← 接口请求
```
## 开发要点
1. **一个模块一个目录**:放在 `ui/src/views/{group}/{module}/` 下
2. **4 个文件各司其职**:主页面、编辑弹窗、列定义、搜索 Schema
3. **编辑弹窗是独立组件**:通过 Props 接收 ID,通过 Emit 通知父页面
4. **列定义使用 `customRender` 渲染复杂内容**:如状态标签、操作按钮
5. **搜索 Schema 使用 `FormSchema` 类型**:配置化定义表单字段
6. **新增/编辑通过 `positionId` 区分**:0 为新增,非 0 为编辑
7. **防重复提交使用 `useLockFn`**:提交时按钮 loading
## 总结
前端页面统一使用 `PageWrapper` + `BasicForm` + `BasicTable` + `bodyCell` 插槽四件套,分页由 `BasicTable` 内置处理。每个模块包含 `index.vue`(列表页)、`edit.vue`(编辑弹窗)、`columns.ts`(列定义)、`querySchemas.ts`(搜索配置)四个文件。