# 前端页面视图 前端页面视图是用户直接交互的界面。项目使用 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` 导入。`submitOnReset: true` 表示重置时自动触发查询。 **数据表格**:通过 `BasicTable` 组件渲染,`request` 属性绑定数据加载函数,`columns` 从 `columns.ts` 导入。 **操作栏**:通过 `actionColumn` 的 `render` 函数使用 `h()` 渲染 `TableAction`,支持权限控制(`auth`)。图标使用 `renderIcon` 工具函数渲染 `@vicons/antd` 图标。 **编辑弹窗**:使用 `defineAsyncComponent` 懒加载 `edit.vue`,通过 `v-model:visible` 控制显示,`positionId` 传入编辑记录 ID(0 表示新增)。 **删除逻辑**:使用 NaiveUI 的 `useDialog` 显示确认弹窗,单条删除传入 `record`,批量删除从 `rowKeys` 取已选 ID。 ## 2. 编辑弹窗 `edit.vue` 编辑弹窗是独立组件,负责新增和编辑操作的表单展示与提交。 ### 完整代码 ```vue ``` ### 代码解析 **Props 接收**: - `visible`:控制弹窗显示/隐藏,通过 `v-model:visible` 双向绑定 - `positionId`:编辑记录的 ID,为 0 时表示新增模式 **表单数据**:使用 `reactive` 定义,字段与后端 Schema 对应。 **新增/编辑判断**:`props.positionId ? positionUpdate : positionAdd`,通过 ID 是否为 0 区分。 **防重复提交**:`useModal` 提供 `setSubLoading` 控制提交按钮 loading 状态。 **数据回填**:`onMounted` 时判断如果是编辑模式,调用 `getPositionDetail` 获取数据并填充表单。 **事件通信**: - `emit('update:visible', false)`:关闭弹窗 - `emit('success')`:通知父页面刷新表格 ## 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, }, ]; ``` ### 代码解析 **选择列**:`type: 'selection'` 自动添加复选框列,配合 `@update:checked-row-keys` 事件实现批量选择。 **固定列**:`fixed: 'left'` 固定在左侧,`fixed: 'right'` 固定在右侧(操作列)。 **自定义渲染**:`render(record)` 使用 Vue 的 `h()` 函数创建虚拟 DOM。`render` 函数直接接收行数据对象,通过 `record.key` 访问字段值(如 `record.status`)。状态列将 `1/2` 渲染为绿色/红色标签。 **宽度设置**: - `width`:固定宽度 - `minWidth`:最小宽度,可自适应拉伸 ## 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` 等 | | `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 通知父页面 4. **列定义使用 `h()` 渲染复杂内容**:如状态标签、操作按钮 5. **搜索 Schema 使用 `FormSchema` 类型**:配置化定义表单字段 6. **新增/编辑通过 `positionId` 区分**:0 为新增,非 0 为编辑 7. **删除使用 `useDialog` 确认弹窗**:NaiveUI 命令式对话框 ## 总结 前端页面统一使用 `n-card` + `BasicForm` + `BasicTable` + `TableAction` 四件套,分页由 `BasicTable` 内置处理。每个模块包含 `index.vue`(列表页)、`edit.vue`(编辑弹窗)、`columns.ts`(列定义)、`querySchemas.ts`(搜索配置)四个文件。