前端开发指南
无论后端是MVC还是Webflux, 前端的开发实现都是一致的, 本节就以项目成员管理模块为例说明如何实现前端的全量代码开发. 最终效果如下图所示:


1. 项目概述
项目成员管理模块是一个典型的CRUD(增删改查)功能模块,主要实现:
- 成员列表展示与分页
- 成员信息的新增、编辑、删除
- 搜索和筛选功能
- 批量操作
核心文件结构:
member/
├── index.vue # 主页面组件
├── member-drawer.vue # 表单抽屉组件
└── types.ts # 类型定义(可选)2. 技术栈与依赖
核心技术栈
- Vue 3 + Composition API - 响应式框架
- TypeScript - 类型安全
- Ant Design Vue - UI组件库
- VxeGrid - 高性能表格组件
- Vben Admin - 管理后台框架
主要依赖组件
typescript
import { useVbenDrawer, useVbenForm, useVbenVxeGrid } from '@vben/common-ui'
import { Button, InputSearch, Select, Modal, message } from 'ant-design-vue'3. 项目结构
src/views/project/member/
├── index.vue # 主页面 - 列表展示和操作
├── member-drawer.vue # 抽屉组件 - 表单处理
└── types.ts # 类型定义文件
src/api/project/
└── member.ts # API接口定义
src/locales/
└── lang/ # 国际化文件4. 开发步骤
4.1 API接口准备
首先定义所需的API接口:
typescript
// 主要接口
- getProjectMemberListApi() // 获取成员列表
- saveProjectMemberApi() // 保存成员信息
- deleteProjectMemberApi() // 删除成员
- getOptionsApi() // 获取选项数据(角色、性别等)关键点:
- API返回的数据格式要与前端组件期望的格式一致
- 分页参数要符合VxeGrid的要求
- 错误处理机制要完善
4.2 主页面开发
4.2.1 组件导入和基础设置
vue
<script lang="ts" setup>
import type { VxeGridProps } from '#/adapter';
import { ref, onMounted } from 'vue';
import { Page, useVbenDrawer } from '@vben/common-ui';
import { Button, InputSearch, Select, Modal, message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter';
</script>4.2.2 抽屉组件配置
typescript
const [Drawer, drawerApi] = useVbenDrawer({
connectedComponent: MemberDrawer,
});
function openDrawer(row?: any) {
drawerApi.setData(row || {});
drawerApi.open();
}关键点:
connectedComponent连接表单组件setData()传递数据给抽屉组件- 区分新增和编辑模式
4.2.3 表格配置
typescript
const gridOptions: VxeGridProps<MemberType> = {
columns: [
{ type: 'checkbox', width: 40 }, // 选择框
{ title: '序号', type: 'seq', width: 50 }, // 序号
{ field: 'name', title: '姓名' }, // 数据字段
{
field: 'action',
fixed: 'right',
slots: { default: 'action' }, // 操作列插槽
title: '操作',
width: 140,
},
],
proxyConfig: {
ajax: {
query: async ({ page }) => {
return await getProjectMemberListApi({
page: page.currentPage,
pageSize: page.pageSize,
searchKey: searchText.value,
role: selectedRole.value,
sex: selectedSex.value,
});
},
},
},
};关键点:
proxyConfig配置自动分页和数据加载slots自定义操作列- 筛选参数实时传递给API
4.2.4 搜索和筛选功能
typescript
const searchText = ref('');
const selectedRole = ref('');
const selectedSex = ref('');
function handleSearch() {
gridApi.reload();
}4.2.5 批量操作功能
typescript
const selectedRows = ref<MemberType[]>([]);
const gridEvents = {
checkboxChange({ records }: { records: any[] }) {
selectedRows.value = records;
},
checkboxAll({ records }: { records: any[] }) {
selectedRows.value = records;
},
};
async function handleDeleteSelected() {
if (selectedRows.value.length === 0) return;
Modal.confirm({
title: '删除确认',
content: `确定要删除选中的 ${selectedRows.value.length} 条记录吗?`,
async onOk() {
try {
await Promise.all(selectedRows.value.map(row => deleteProjectMemberApi(row.id)));
selectedRows.value = [];
await gridApi.reload();
message.success('删除成功');
} catch {
message.error('删除失败');
}
}
});
}4.2.6 选项数据加载
typescript
const roleOptions = ref<Option[]>([]);
const sexOptions = ref<Option[]>([]);
async function loadRoleOptions() {
try {
const data = await getOptionsApi({
pathOrBeanName: 'com.matrix.app.common.enums.Role'
});
roleOptions.value = data;
} catch (error) {
console.error('加载角色选项失败', error);
}
}
onMounted(() => {
loadRoleOptions();
loadSexOptions();
});4.3 表单抽屉组件开发
4.3.1 属性定义
typescript
const props = defineProps<{
onSave: () => void;
roleOptions: { label: string; value: string }[];
sexOptions: { label: string; value: string }[];
}>();4.3.2 表单配置
typescript
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
},
handleSubmit: onSubmit,
schema: [
{
component: 'Input',
componentProps: { type: 'hidden' },
fieldName: 'id', // 隐藏ID字段
},
{
component: 'Input',
componentProps: {
placeholder: '请输入姓名',
},
fieldName: 'name',
label: '姓名',
rules: 'required', // 必填验证
},
{
component: 'Select',
componentProps: () => ({
placeholder: '请选择角色',
options: props.roleOptions, // 动态选项
allowClear: true,
}),
fieldName: 'role',
label: '角色',
rules: 'required',
},
// ... 其他字段
],
showDefaultActions: false, // 隐藏默认操作按钮
});关键点:
- 使用函数形式的
componentProps获取动态数据 rules配置表单验证showDefaultActions: false自定义操作按钮
4.3.3 抽屉配置
typescript
const [Drawer, drawerApi] = useVbenDrawer({
onCancel() {
drawerApi.close();
},
onConfirm: async () => {
const isValid = await formApi.validate();
if (isValid.valid) {
try {
await formApi.submitForm();
drawerApi.close();
message.success(values.id ? '编辑成功' : '新增成功');
props.onSave();
} catch (error: any) {
message.error(error.message || '提交失败');
}
} else {
message.error('请填写必填项');
}
},
onOpenChange(isOpen: boolean) {
if (isOpen) {
const values = drawerApi.getData<Record<string, any>>();
if (values) {
formApi.setValues(values); // 编辑时设置表单值
}
} else {
formApi.setValues({}); // 关闭时清空表单
}
},
});4.4 数据交互与状态管理
4.4.1 数据流转
主页面 -> 点击新增/编辑 -> 抽屉组件
抽屉组件 -> 提交表单 -> API请求 -> 回调主页面 -> 刷新列表4.4.2 状态同步
typescript
// 主页面
function handleSave() {
gridApi.reload(); // 重新加载列表数据
}
// 抽屉组件
props.onSave(); // 通知主页面刷新5. 关键技术点
5.1 组件通信
- Props传递: 父组件向子组件传递选项数据
- 事件回调: 子组件通过回调通知父组件更新数据
- 抽屉数据传递: 通过
drawerApi.setData()和drawerApi.getData()
5.2 表单处理
- 动态Schema: 使用函数形式的配置支持响应式数据
- 表单验证: 使用内置的
rules进行验证 - 数据双向绑定: 自动处理表单数据的获取和设置
5.3 表格功能
- 自动分页: 通过
proxyConfig配置 - 批量选择: 通过
checkbox类型列和事件处理 - 实时搜索: 搜索条件变化时自动重新加载数据
5.4 国际化支持
typescript
import { $t } from '#/locales';
// 使用方式
title: $t('member.name')
placeholder: $t('member.form.name.placeholder')6. 挂载菜单
菜单挂载无需编码, 运行矩阵星云前后端工程后, 直接在界面上输入即可. 如下图所示: 
7. 最佳实践
7.1 错误处理
typescript
try {
await saveProjectMemberApi(data);
} catch (error: any) {
console.error('保存失败:', error);
throw new Error(error.message || '提交失败');
}7.2 Loading状态
- VxeGrid 自带 loading 效果
- 抽屉提交时会自动显示 loading
7.3 数据类型安全
typescript
interface MemberType {
id: string;
name: string;
sex: string;
role: string;
}
const gridOptions: VxeGridProps<MemberType> = { ... }7.4 组件复用
- 抽屉组件可以被其他页面引用
- 表单Schema可以提取为配置文件
8. 常见问题
8.1 表单数据不更新
问题: 编辑时表单显示的还是上次的数据 解决: 确保在 onOpenChange 中正确设置和清空表单数据
8.2 选项数据为空
问题: 下拉选项不显示 解决: 检查选项数据的加载时机和数据格式
8.3 批量删除不生效
问题: 选中数据但删除失败 解决: 检查 gridEvents 的事件绑定和 selectedRows 的更新
8.4 分页问题
问题: 分页参数传递错误 解决: 确保API接口的分页参数名称与 proxyConfig 中的一致
9. 扩展功能
9.1 导出功能
typescript
async function handleExport() {
// 使用VxeGrid的导出功能
await gridApi.exportData({
filename: 'members',
type: 'xlsx'
});
}9.2 高级搜索
typescript
// 添加更多搜索条件
const advancedSearch = ref({
dateRange: [],
status: '',
department: ''
});9.3 权限控制
typescript
// 根据用户权限显示操作按钮
<Button v-if="hasPermission('member:add')" type="primary">
新增
</Button>9.4 数据校验增强
typescript
// 自定义验证规则
{
fieldName: 'phone',
label: '手机号',
rules: [
{ required: true, message: '请输入手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' }
],
}总结
这个member模块展示了一个完整的CRUD功能实现,包含了现代Vue 3项目中的最佳实践。通过这个例子,开发者可以掌握:
- 组件化开发思维 - 主页面与表单组件分离
- 数据驱动 - 通过配置化的方式构建表格和表单
- 用户体验 - 完善的加载状态、错误提示、操作确认
- 代码复用 - 抽屉组件、表单配置可以在多个模块中复用
- 类型安全 - 完整的TypeScript类型定义
按照这个模式,可以快速开发出类似的功能模块,只需要修改字段定义、API接口和业务逻辑即可。
