# 前端页面视图
前端页面视图是用户直接交互的界面。项目使用 Vue3 + NaiveUI + Vite 构建。一个完整的 CRUD 页面由 4 个文件组成:
```
ui/src/views/system/position/
├── index.vue # 主页面:搜索 + 表格 + 操作
├── edit.vue # 编辑弹窗:新增/编辑表单
├── columns.ts # 表格列定义
└── querySchemas.ts # 搜索表单 Schema
```
## 1. 主页面 `index.vue`
主页面是模块的入口页面,包含搜索表单、数据表格和操作按钮。
### 完整代码
```vue
```
### 代码解析
**页面结构**:`div` 包裹两个 `n-card`,上方是搜索表单,下方是数据表格。
**搜索表单**:通过 `useForm` 注册,Schema 从 `querySchemas.ts` 导入。`gridProps` 控制响应式列数。
**数据表格**:通过 `BasicTable` 组件渲染,`request` 属性绑定数据加载函数,`columns` 从 `columns.ts` 导入。
**操作栏**:通过 `actionColumn` 的 `render` 函数使用 `h()` 渲染 `TableAction`,支持权限控制(`auth`)。图标通过 `renderIcon` 工具函数包装。
**编辑弹窗**:使用 `editDialog` 组件,通过 `v-model:visible` 控制显示,`positionId` 传入编辑记录 ID(0 表示新增)。通过 `ref` 调用 `openModal()` 打开弹窗。
**删除逻辑**:使用 NaiveUI 的 `useDialog` 弹出确认对话框,单条删除传入 `record`,批量删除从 `rowKeys` 取已选 ID。
**消息提示**:使用 NaiveUI 的 `useMessage` 组件,调用 `message.success()` 显示成功提示。
## 2. 编辑弹窗 `edit.vue`
编辑弹窗是独立组件,负责新增和编辑操作的表单展示与提交。
### 完整代码
```vue
正常
停用
```
### 代码解析
**弹窗组件**:使用项目封装的 `basicModal` 组件,通过 `useModal` hook 管理弹窗状态。
**Props 接收**:
- `visible`:控制弹窗显示/隐藏,通过 `v-model:visible` 双向绑定
- `positionId`:编辑记录的 ID,为 0 时表示新增模式
**表单组件**:使用 NaiveUI 的 `n-form`、`n-form-item`、`n-input`、`n-radio-group`、`n-radio`、`n-input-number` 组件。
**表单配置**:
- `label-placement="left"`:标签在左侧显示
- `label-width="85px"`:标签宽度
- `path`:表单字段路径,用于校验
- `:rule`:校验规则
**新增/编辑判断**:`props.positionId ? positionUpdate : positionAdd`,通过 ID 是否为 0 区分。
**防重复提交**:`useModal` 的 `setSubLoading` 方法控制提交按钮 loading 状态。
**数据回填**:`onMounted` 时判断如果是编辑模式,调用 `getPositionDetail` 获取数据并填充表单。
**事件通信**:
- `emit('update:visible', false)`:关闭弹窗
- `emit('success')`:通知父页面刷新表格
**弹窗调用方式**:父页面通过 `ref` 调用 `openModal()` 打开弹窗。
## 3. 表格列定义 `columns.ts`
定义表格的列配置,包括列名、字段名、宽度和自定义渲染。
### 完整代码
```typescript
import { h } from 'vue';
import { NTag } from 'naive-ui';
export const columns = [
{
type: 'selection',
width: 50,
fixed: 'left',
},
{
title: 'ID',
key: 'id',
fixed: 'left',
width: 50,
},
{
title: '岗位名称',
key: 'name',
width: 100,
},
{
title: '岗位状态',
key: 'status',
width: 100,
render(record) {
return h(
NTag,
{
style: {
marginRight: '6px',
},
bordered: false,
type: record.status == 1 ? 'success' : 'error',
},
{
default: () => (record.status == 1 ? '正常' : '停用'),
},
);
},
},
{
title: '排序',
key: 'sort',
width: 100,
},
{
title: '创建人',
key: 'createUser',
width: 100,
},
{
title: '创建时间',
key: 'createTime',
width: 180,
},
];
```
### 代码解析
**列配置字段**:NaiveUI 表格使用 `title`(列标题)和 `key`(字段名)。
**选择列**:`type: 'selection'` 自动添加复选框列,配合 `@update:checked-row-keys` 事件实现批量选择。
**固定列**:`fixed: 'left'` 固定在左侧,`fixed: 'right'` 固定在右侧(操作列)。
**自定义渲染**:`render(record)` 使用 Vue 的 `h()` 函数创建虚拟 DOM。注意 NaiveUI 的 `render` 回调直接接收 `record` 对象,无需通过 `record.row` 访问。
**标签组件**:使用 NaiveUI 的 `NTag` 组件,`type` 属性支持 `'success'`、`'error'`、`'warning'`、`'info'` 等类型。`bordered: false` 去除边框。
**宽度设置**:
- `width`:固定宽度
## 4. 搜索表单 `querySchemas.ts`
定义搜索表单的字段配置,使用项目封装的 `FormSchema` 类型。
### 完整代码
```typescript
import { FormSchema } from '@/components/Form/index';
export const schemas: FormSchema[] = [
{
field: 'name',
component: 'NInput',
label: '岗位名称',
componentProps: {
placeholder: '请输入岗位名称',
},
},
{
field: 'status',
component: 'NSelect',
label: '状态',
componentProps: {
placeholder: '请选择状态',
clearable: true,
options: [
{
label: '正常',
value: '1',
},
{
label: '停用',
value: '2',
},
],
},
},
];
```
### 代码解析
**FormSchema 字段**:
| 字段 | 说明 |
|:----|:----|
| `field` | 字段名,与后端查询参数对应 |
| `component` | 表单组件类型:`NInput` / `NSelect` / `NDatePicker` 等(NaiveUI 组件名) |
| `label` | 标签文本 |
| `componentProps` | 组件属性,透传给 NaiveUI 组件 |
**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 通知父页面,通过 `useModal` 管理弹窗状态
4. **列定义使用 `h()` 渲染复杂内容**:如状态标签、操作按钮
5. **搜索 Schema 使用 `FormSchema` 类型**:配置化定义表单字段,组件名使用 NaiveUI 组件名(`NInput`、`NSelect`)
6. **新增/编辑通过 `positionId` 区分**:0 为新增,非 0 为编辑
7. **消息提示使用 `useMessage()`**:调用 `message.success()` / `message.error()` 等
8. **确认对话框使用 `useDialog()`**:调用 `dialog.warning()` 等,通过 `onPositiveClick` 回调处理确认
9. **表格行选择使用 `@update:checked-row-keys`**:获取选中行的 key 数组
10. **列配置使用 `title` 和 `key`**:定义表格列标题和字段名
## 总结
前端页面统一使用 `n-card` + `BasicForm` + `BasicTable` + `TableAction` 组件组合,分页由 `BasicTable` 内置处理。每个模块包含 `index.vue`(列表页)、`edit.vue`(编辑弹窗)、`columns.ts`(列定义)、`querySchemas.ts`(搜索配置)四个文件。编辑弹窗使用项目封装的 `basicModal` 组件,通过 `useModal` hook 管理弹窗状态和提交按钮 loading。