# 前端页面视图
前端页面视图是用户直接交互的界面。项目使用 Vue3 + NaiveUI + Vite 构建。一个完整的 CRUD 页面由 5 个文件组成:
```
ui/src/views/tool/example/
├── index.vue # 主页面:搜索 + 表格 + 操作
├── edit.vue # 编辑弹窗:新增/编辑表单
├── detail.vue # 详情弹窗:查看记录详情
├── columns.ts # 表格列定义
└── querySchemas.ts # 搜索表单 Schema
```
## 1. 主页面 index.vue
主页面是模块的入口页面,包含搜索表单、数据表格和操作按钮。
### 核心结构
```vue
```
### 核心逻辑
```vue
```
### 关键点
- **n-card**:NaiveUI 卡片容器,提供统一的页面布局
- **BasicForm**:搜索表单组件,通过 `useForm` 注册,`gridProps` 配置响应式栅格
- **BasicTable**:数据表格组件,内置分页功能,使用 `@update:checked-row-keys` 监听多选
- **TableAction**:操作栏组件,图标通过 `renderIcon` 包装 `@vicons/antd` 图标
- **useMessage / useDialog**:NaiveUI 的消息和对话框 Hook
- **v-perm**:权限指令,控制按钮显示
## 2. 编辑弹窗 edit.vue
编辑弹窗是独立组件,负责新增和编辑操作的表单展示与提交。使用 `basicModal` + `useModal` 模式。
### 核心结构
```vue
(formData.avatarImgName = name)"
:fileType="['image/jpeg', 'image/png', 'image/jpg', 'image/gif', 'image/webp']"
name="example"
:fileSize="20"
v-model:image-url="formData.avatar"
>
支持扩展名: jpg png jpeg gif webp; 文件大小不超过20M
正常
禁用
```
### 核心逻辑
```vue
```
### 关键点
- **basicModal + useModal**:NaiveUI 弹窗模式,通过 `useModal` 注册弹窗
- **n-form + n-form-item**:NaiveUI 表单组件,使用 `path` 指定字段,`:rule` 定义校验规则
- **n-select + :options**:NaiveUI 下拉选择,选项通过 `:options` 传入
- **n-radio-group + n-radio**:NaiveUI 单选组件
- **n-input-number**:NaiveUI 数字输入组件
- **useMessage**:NaiveUI 消息 Hook
- **setSubLoading**:通过 `useModal` 控制弹窗确认按钮的 loading 状态
- **openModal / closeModal**:通过 `useModal` 返回的方法控制弹窗显隐
## 3. 详情弹窗 detail.vue
详情弹窗用于展示记录的详细信息,使用 NaiveUI 的 `n-descriptions` 组件。
### 核心结构
```vue
{{ formData.name || '-' }}
-
{{ getChoiceLabel('type', formData.type) }}
{{ getChoiceLabel('status', formData.status) }}
{{ formData.sort || '-' }}
关闭
```
### 核心逻辑
```vue
```
### 关键点
- **basicModal**:使用 `basicModal` + `useModal` 注册弹窗
- **n-descriptions**:NaiveUI 的描述列表组件,用于展示详情
- **n-image**:NaiveUI 的图片组件,支持预览功能
- **getChoiceLabel**:将枚举值转换为中文标签
## 4. 表格列定义 columns.ts
定义表格的列配置,包括列名、字段名、宽度和自定义渲染。
### 完整代码
```typescript
import { h } from 'vue';
import { NTag, NAvatar, NImage } from 'naive-ui';
export const columns = [
{
type: 'selection', // 多选框列
width: 50,
fixed: 'left',
},
{
title: 'ID',
key: 'id',
fixed: 'left',
width: 50,
},
{
title: '案例名称',
key: 'name',
width: 150,
ellipsis: { tooltip: true },
},
{
title: '案例图片',
key: 'avatar',
width: 150,
render(record) {
return record.avatar
? h(NAvatar, {
src: record.avatar,
size: 48,
shape: 'square',
fit: 'cover',
style: { borderRadius: '4px' },
})
: h('span', '-');
},
},
{
title: '案例类型',
key: 'type',
width: 100,
render(record) {
const statusMap = {
1: { text: '类型1', type: 'success' },
2: { text: '类型2', type: 'error' },
3: { text: '类型3', type: 'warning' },
4: { text: '类型4', type: 'info' },
};
const status = statusMap[record.type];
return h(
NTag,
{ bordered: false, type: status?.type || 'info' },
{ default: () => status?.text || record.type },
);
},
},
{
title: '案例状态',
key: 'status',
width: 100,
render(record) {
const statusMap = {
1: { text: '正常', type: 'success' },
2: { text: '禁用', type: 'error' },
};
const status = statusMap[record.status];
return h(
NTag,
{ bordered: false, type: status?.type || 'info' },
{ default: () => status?.text || record.status },
);
},
},
{ title: '排序', key: 'sort', width: 100, ellipsis: { tooltip: true } },
{ title: '创建人', key: 'createUser', width: 100 },
{ title: '创建时间', key: 'createTime', width: 180 },
];
```
### 关键点
- **title / key**:NaiveUI 列定义使用 `title` 和 `key` 指定列标题和字段名
- **type: 'selection'**:自动添加复选框列
- **render 函数**:使用 Vue 的 `h()` 函数创建虚拟 DOM,`record` 直接是行数据
- **NTag 组件**:将状态值渲染为彩色标签
- **NAvatar 组件**:将图片 URL 渲染为缩略图
- **ellipsis**:NaiveUI 使用 `ellipsis: { tooltip: true }` 实现溢出省略
## 5. 搜索表单 querySchemas.ts
定义搜索表单的字段配置,使用项目封装的 `FormSchema` 类型。
### 完整代码
```typescript
import { FormSchema } from '@/components/Form/index';
export const schemas: FormSchema[] = [
{
field: 'name',
component: 'NInput',
label: '案例名称',
componentProps: {
placeholder: '请输入案例名称',
clearable: true,
},
},
{
field: 'type',
component: 'NSelect',
label: '案例类型',
componentProps: {
placeholder: '请输入案例类型',
clearable: true,
filterable: true,
options: [
{ label: '类型1', value: 1 },
{ label: '类型2', value: 2 },
{ label: '类型3', value: 3 },
{ label: '类型4', value: 4 },
],
},
},
{
field: 'status',
component: 'NSelect',
label: '案例状态',
componentProps: {
placeholder: '请输入案例状态',
clearable: true,
filterable: true,
options: [
{ label: '正常', value: 1 },
{ label: '禁用', value: 2 },
],
},
},
];
```
### 关键点
- **field**:字段名,与后端查询参数对应
- **component**:表单组件类型使用 NaiveUI 组件名:`NInput` / `NSelect`
- **componentProps**:组件属性,透传给 NaiveUI 组件
- **options**:NSelect 组件的下拉选项
## 文件关系图
```
index.vue(主页面)
├── import schemas from './querySchemas' ← 搜索表单配置
├── import columns from './columns' ← 表格列配置
├── import editDialog from './edit.vue' ← 编辑弹窗组件
├── import detailDialog from './detail.vue' ← 详情弹窗组件
└── import API from '@/api/tool/example' ← 接口请求
edit.vue(编辑弹窗)
└── import API from '@/api/tool/example' ← 接口请求
detail.vue(详情弹窗)
└── import API from '@/api/tool/example' ← 接口请求
```
## 开发要点
1. **一个模块一个目录**:放在 `ui/src/views/{group}/{module}/` 下
2. **5 个文件各司其职**:主页面、编辑弹窗、详情弹窗、列定义、搜索 Schema
3. **弹窗使用 basicModal + useModal**:通过 `useModal` 注册,`openModal()` 打开
4. **列定义使用 `h()` 渲染复杂内容**:如 NTag 状态标签、NAvatar 图片缩略图
5. **列定义使用 `title` / `key`**:指定列标题和字段名
6. **搜索 Schema 使用 `NInput` / `NSelect` 组件名**:NaiveUI 表单组件
7. **消息使用 `useMessage()` Hook**:NaiveUI 消息提示
8. **对话框使用 `useDialog()` Hook**:NaiveUI 确认对话框
## 总结
前端页面统一使用 `n-card` + `BasicForm` + `BasicTable` + `TableAction` 组件组合,分页由 `BasicTable` 内置处理。编辑弹窗使用 `basicModal` + `useModal` 模式。每个模块包含 `index.vue`(列表页)、`edit.vue`(编辑弹窗)、`detail.vue`(详情弹窗)、`columns.ts`(列定义)、`querySchemas.ts`(搜索配置)五个文件。