Vue3(篇三) Element Plus 目录前言一、Element Plus 是什么1.1 UI 组件库的作用1.2 为什么选择 Element Plus二、安装与配置2.1 安装2.2 完整引入2.3 按需引入推荐三、表格组件3.1 基础表格3.2 自定义列内容3.3 加载状态四、分页组件4.1 基础分页4.2 后端分页真实场景五、搜索与删除5.1 搜索功能5.2 删除功能5.3 批量删除六、新增与修改6.1 对话框组件6.2 表单组件6.3 表单回显与重置七、子组件通信7.1 定义子组件7.2 父组件使用子组件7.3 父子组件通信总结八、完整案例学生管理页面总结前言在与后端交互axios与请求封装中我们掌握了前端与后端 API 交互的方式。现在数据能获取到了但如何优雅地展示和管理这些数据呢直接用原生 HTML 标签table、form可以实现功能但样式简陋、交互生硬还需要自己写大量 CSS 和 JavaScript。在实际开发中我们通常使用UI 组件库来快速构建美观、交互友好的界面。Element Plus是 Vue3 生态中最成熟的 UI 组件库提供了表格、表单、对话框、分页等常用组件开箱即用。本文将通过一个学生管理系统案例讲解 Element Plus 的核心组件和实战技巧包括表格、分页、搜索、增删改、表单校验、子组件通信等。一、Element Plus 是什么1.1 UI 组件库的作用如果把开发比作装修原生 HTML/CSS是自己买材料、自己设计、自己施工UI 组件库是精装修套餐——样式美观、交互完善、开箱即用组件库提供的典型组件类别组件作用基础Button、Icon、Link按钮、图标、链接表单Input、Select、Checkbox、Radio、Switch各类输入控件数据Table、Pagination、Tree、Tag数据展示反馈Message、Dialog、Notification、Loading提示反馈导航Menu、Tabs、Breadcrumb页面导航布局Container、Layout、Grid页面布局1.2 为什么选择 Element Plus优势说明Vue3 原生支持基于 Vue3 开发完美支持组合式 API组件丰富60 组件覆盖绝大多数场景文档完善中文文档详细示例丰富生态成熟社区活跃问题容易找到解决方案企业级饿了么团队开发经受过大规模项目验证类比 JavaElement Plus 类似于 Java 的前端 UI 框架如 Apache Wicket 或 JSF 组件库但更现代化。二、安装与配置2.1 安装npminstallelement-plus2.2 完整引入在main.ts中引入 Element Plus 及其样式import{createApp}fromvueimportElementPlusfromelement-plusimportelement-plus/dist/index.cssimportAppfrom./App.vueconstappcreateApp(App)app.use(ElementPlus)app.mount(#app)2.3 按需引入推荐完整引入会导致打包体积较大。推荐使用按需引入只打包用到的组件。安装自动导入插件npminstall-Dunplugin-vue-components unplugin-auto-import配置vite.config.tsimport{defineConfig}fromviteimportvuefromvitejs/plugin-vueimportAutoImportfromunplugin-auto-import/viteimportComponentsfromunplugin-vue-components/viteimport{ElementPlusResolver}fromunplugin-vue-components/resolversexportdefaultdefineConfig({plugins:[vue(),AutoImport({resolvers:[ElementPlusResolver()]}),Components({resolvers:[ElementPlusResolver()]})]})配置后无需手动 import直接使用组件即可template !-- 自动导入无需 import -- el-button typeprimary按钮/el-button /template三、表格组件表格是后台管理系统最常用的组件。Element Plus 的el-table功能强大支持排序、筛选、固定列、自定义列等。3.1 基础表格script setup langts import { ref } from vue interface Student { id: number name: string sex: string age: number } const students refStudent[]([ { id: 1, name: 张三, sex: 男, age: 18 }, { id: 2, name: 李四, sex: 女, age: 19 }, { id: 3, name: 王五, sex: 男, age: 20 } ]) /script template el-table :datastudents border stripe stylewidth: 100% el-table-column propid label编号 width100 / el-table-column propname label姓名 / el-table-column propsex label性别 / el-table-column propage label年龄 / /el-table /template属性说明:data表格数据数组border显示边框stripe斑马纹prop列对应的字段名label列标题width列宽度3.2 自定义列内容有时需要在列中显示复杂内容如按钮、格式化文本template el-table :datastudents border stripe el-table-column propid label编号 width100 / el-table-column propname label姓名 !-- 自定义列通过 #default 获取当前行数据 -- template #default{ row } span :style{ color: row.sex 女 ? pink : blue } {{ row.name }} /span /template /el-table-column el-table-column propsex label性别 / el-table-column label操作 template #default{ row } el-button typeprimary sizesmall clickhandleEdit(row)编辑/el-button el-button typedanger sizesmall clickhandleDelete(row.id)删除/el-button /template /el-table-column /el-table /template script setup langts function handleEdit(row: Student) { console.log(编辑:, row) } function handleDelete(id: number) { console.log(删除:, id) } /script重要#default{ row }是插槽语法row是当前行的数据对象。3.3 加载状态从后端获取数据时显示加载状态script setup langts import { ref, onMounted } from vue import request from /api/request const students refStudent[]([]) const loading ref(false) async function getStudents() { loading.value true try { const data await request.getStudent[](/api/students) students.value data } finally { loading.value false } } onMounted(() { getStudents() }) /script template el-table :datastudents border stripe v-loadingloading el-table-column propid label编号 / el-table-column propname label姓名 / !-- ... -- /el-table /template四、分页组件数据量大时需要分页展示。Element Plus 的el-pagination与表格配合使用。4.1 基础分页script setup langts import { ref, computed } from vue const allStudents refStudent[]([ // 假设有 100 条数据... ]) const currentPage ref(1) const pageSize ref(10) // 当前页数据 const currentPageData computed(() { const start (currentPage.value - 1) * pageSize.value const end start pageSize.value return allStudents.value.slice(start, end) }) function handlePageChange(page: number) { currentPage.value page } function handleSizeChange(size: number) { pageSize.value size currentPage.value 1 // 切换每页条数时重置到第一页 } /script template el-table :datacurrentPageData border stripe !-- 列定义... -- /el-table el-pagination v-model:current-pagecurrentPage v-model:page-sizepageSize :page-sizes[10, 20, 50, 100] :totalallStudents.length layouttotal, sizes, prev, pager, next, jumper size-changehandleSizeChange current-changehandlePageChange / /template属性/事件说明v-model:current-page当前页码v-model:page-size每页条数:page-sizes每页条数选项:total总记录数layout分页布局元素size-change每页条数改变事件current-change页码改变事件4.2 后端分页真实场景实际项目中分页通常由后端实现前端只传递分页参数script setup langts import { ref, onMounted } from vue import { useRequest } from vue-request import request from /api/request interface PageResultT { list: T[] total: number } const queryParams ref({ name: , page: 1, size: 10 }) const { data, total, loading, run } useRequestPageResultStudent( () request.get(/api/students/page, { params: queryParams.value }), { defaultParams: [queryParams.value], pagination: { currentKey: page, pageSizeKey: size, totalKey: data.total } } ) function handlePageChange(page: number) { queryParams.value.page page run(queryParams.value) } function handleSizeChange(size: number) { queryParams.value.size size queryParams.value.page 1 run(queryParams.value) } /script template el-table :datadata?.list || [] border stripe v-loadingloading el-table-column propid label编号 / el-table-column propname label姓名 / !-- ... -- /el-table el-pagination v-model:current-pagequeryParams.page v-model:page-sizequeryParams.size :page-sizes[10, 20, 50] :totaltotal layouttotal, sizes, prev, pager, next size-changehandleSizeChange current-changehandlePageChange / /template五、搜索与删除5.1 搜索功能script setup langts import { ref } from vue const queryParams ref({ name: , sex: , ageRange: }) const { data, loading, run } useRequest( () request.get(/api/students/page, { params: queryParams.value }) ) function handleSearch() { queryParams.value.page 1 // 搜索时重置到第一页 run() } /script template div classsearch-bar el-input v-modelqueryParams.name placeholder姓名 stylewidth: 150px clearable / el-select v-modelqueryParams.sex placeholder性别 stylewidth: 100px clearable el-option label男 value男 / el-option label女 value女 / /el-select el-select v-modelqueryParams.ageRange placeholder年龄 stylewidth: 120px clearable el-option label18岁以下 value0,18 / el-option label18-25岁 value18,25 / el-option label25岁以上 value25,100 / /el-select el-button typeprimary clickhandleSearch搜索/el-button /div el-table :datadata?.list || [] border stripe v-loadingloading !-- 列定义... -- /el-table /template style scoped .search-bar { margin-bottom: 16px; display: flex; gap: 8px; } /style5.2 删除功能使用ElMessageBox确认删除script setup langts import { ElMessage, ElMessageBox } from element-plus async function handleDelete(id: number) { try { // 确认框 await ElMessageBox.confirm(确认删除该学生, 提示, { confirmButtonText: 确定, cancelButtonText: 取消, type: warning }) // 调用删除接口 await request.delete(/api/students/${id}) // 提示成功 ElMessage.success(删除成功) // 刷新列表 run() } catch (error) { // 用户点击取消不做处理 if (error cancel) return ElMessage.error(删除失败) } } /script5.3 批量删除script setup langts import { ref } from vue const selectedIds refnumber[]([]) // 表格选择变化事件 function handleSelectionChange(selection: Student[]) { selectedIds.value selection.map(s s.id) } async function handleBatchDelete() { if (selectedIds.value.length 0) { ElMessage.warning(请先选择要删除的学生) return } try { await ElMessageBox.confirm(确认删除选中的 ${selectedIds.value.length} 名学生, 提示) await request.delete(/api/students, { data: selectedIds.value }) ElMessage.success(删除成功) selectedIds.value [] run() } catch (error) { if (error ! cancel) { ElMessage.error(删除失败) } } } /script template el-button typedanger clickhandleBatchDelete批量删除/el-button el-table :datadata?.list || [] border stripe selection-changehandleSelectionChange !-- 多选列 -- el-table-column typeselection width55 / el-table-column propid label编号 / !-- ... -- /el-table /template六、新增与修改新增和修改通常使用对话框Dialog 表单Form实现。6.1 对话框组件script setup langts import { ref } from vue const dialogVisible ref(false) const dialogTitle ref(新增学生) // 打开新增对话框 function handleAdd() { dialogTitle.value 新增学生 dialogVisible.value true } // 打开编辑对话框 function handleEdit(row: Student) { dialogTitle.value 编辑学生 // 回填表单数据... dialogVisible.value true } /script template el-button typeprimary clickhandleAdd新增学生/el-button el-dialog v-modeldialogVisible :titledialogTitle width500px !-- 表单内容 -- span这里是表单.../span template #footer el-button clickdialogVisible false取消/el-button el-button typeprimary clickhandleSubmit确定/el-button /template /el-dialog /template6.2 表单组件script setup langts import { ref, reactive } from vue interface StudentForm { id?: number name: string sex: string age: number } const form reactiveStudentForm({ name: , sex: 男, age: 18 }) const formRef ref() const rules { name: [ { required: true, message: 请输入姓名, trigger: blur }, { min: 2, max: 10, message: 长度在 2 到 10 个字符, trigger: blur } ], sex: [ { required: true, message: 请选择性别, trigger: change } ], age: [ { required: true, message: 请输入年龄, trigger: blur }, { type: number, min: 1, max: 150, message: 年龄在 1 到 150 之间, trigger: blur } ] } async function handleSubmit() { try { // 表单校验 await formRef.value.validate() // 提交数据 if (form.id) { await request.put(/api/students/${form.id}, form) ElMessage.success(修改成功) } else { await request.post(/api/students, form) ElMessage.success(新增成功) } dialogVisible.value false run() // 刷新列表 } catch (error) { // 校验失败不做处理 } } /script template el-dialog v-modeldialogVisible :titledialogTitle width500px el-form refformRef :modelform :rulesrules label-width80px el-form-item label姓名 propname el-input v-modelform.name / /el-form-item el-form-item label性别 propsex el-radio-group v-modelform.sex el-radio value男男/el-radio el-radio value女女/el-radio /el-radio-group /el-form-item el-form-item label年龄 propage el-input-number v-modelform.age :min1 :max150 / /el-form-item /el-form template #footer el-button clickdialogVisible false取消/el-button el-button typeprimary clickhandleSubmit确定/el-button /template /el-dialog /templateElement Plus说明类比 Java:model表单数据对象JavaBean:rules校验规则NotNull、Size等注解prop字段名与 rules 对应属性名trigger触发时机-validate()执行校验手动校验6.3 表单回显与重置编辑时需要回显数据新增/编辑后需要重置表单script setup langts import { ref, reactive, nextTick } from vue const form reactiveStudentForm({ name: , sex: 男, age: 18 }) const formRef ref() // 打开新增对话框 function handleAdd() { dialogTitle.value 新增学生 resetForm() dialogVisible.value true } // 打开编辑对话框 async function handleEdit(row: Student) { dialogTitle.value 编辑学生 // 先打开对话框再回填数据避免校验立即触发 dialogVisible.value true // 等待 DOM 更新后再回填 await nextTick() Object.assign(form, row) } // 重置表单 function resetForm() { formRef.value?.resetFields() // 或者手动重置 Object.assign(form, { name: , sex: 男, age: 18 }) } // 提交成功后 async function handleSubmit() { // ...提交逻辑... dialogVisible.value false resetForm() // 重置表单 run() } /script七、子组件通信当对话框表单逻辑复杂时建议将表单抽取为子组件。7.1 定义子组件新建src/components/StudentForm.vuescript setup langts import { ref, reactive, watch } from vue import type { FormInstance } from element-plus interface StudentForm { id?: number name: string sex: string age: number } // 接收父组件传递的属性 const props defineProps{ modelValue: boolean // 对话框显示状态 student?: StudentForm // 编辑时传入的学生数据 }() // 定义事件 const emit defineEmits{ update:modelValue: [value: boolean] success: [] }() const formRef refFormInstance() const form reactiveStudentForm({ name: , sex: 男, age: 18 }) const rules { name: [{ required: true, message: 请输入姓名, trigger: blur }], sex: [{ required: true, message: 请选择性别, trigger: change }], age: [{ required: true, message: 请输入年龄, trigger: blur }] } // 监听 student 变化回填表单 watch( () props.student, (newStudent) { if (newStudent) { Object.assign(form, newStudent) } else { resetForm() } } ) function resetForm() { formRef.value?.resetFields() } async function handleSubmit() { try { await formRef.value?.validate() if (form.id) { await request.put(/api/students/${form.id}, form) ElMessage.success(修改成功) } else { await request.post(/api/students, form) ElMessage.success(新增成功) } emit(update:modelValue, false) emit(success) } catch (error) { // 校验失败 } } function handleCancel() { emit(update:modelValue, false) resetForm() } /script template el-dialog :model-valuemodelValue :titlestudent ? 编辑学生 : 新增学生 width500px closehandleCancel el-form refformRef :modelform :rulesrules label-width80px el-form-item label姓名 propname el-input v-modelform.name / /el-form-item el-form-item label性别 propsex el-radio-group v-modelform.sex el-radio value男男/el-radio el-radio value女女/el-radio /el-radio-group /el-form-item el-form-item label年龄 propage el-input-number v-modelform.age :min1 :max150 / /el-form-item /el-form template #footer el-button clickhandleCancel取消/el-button el-button typeprimary clickhandleSubmit确定/el-button /template /el-dialog /template7.2 父组件使用子组件script setup langts import { ref } from vue import StudentForm from /components/StudentForm.vue const dialogVisible ref(false) const currentStudent refStudent | undefined() // 新增 function handleAdd() { currentStudent.value undefined dialogVisible.value true } // 编辑 function handleEdit(row: Student) { currentStudent.value { ...row } dialogVisible.value true } // 表单提交成功 function handleFormSuccess() { run() // 刷新列表 } /script template el-button typeprimary clickhandleAdd新增学生/el-button el-table :datadata?.list || [] border stripe !-- ... -- el-table-column label操作 template #default{ row } el-button typeprimary sizesmall clickhandleEdit(row)编辑/el-button /template /el-table-column /el-table !-- 子组件 -- StudentForm v-modeldialogVisible :studentcurrentStudent successhandleFormSuccess / /template7.3 父子组件通信总结方式语法说明父传子defineProps父组件通过属性传递数据给子组件子传父defineEmits子组件通过事件传递数据给父组件双向绑定v-model父子组件同步数据如对话框显示状态类比 Java类似于 Java 的接口定义父组件定义接口props events子组件实现接口。八、完整案例学生管理页面将以上内容整合成一个完整的学生管理页面script setup langts import { ref } from vue import { useRequest } from vue-request import { ElMessage, ElMessageBox } from element-plus import request from /api/request import StudentForm from /components/StudentForm.vue interface Student { id: number name: string sex: string age: number } // 查询参数 const queryParams ref({ name: , sex: , page: 1, size: 10 }) // 学生列表 const { data, total, loading, run } useRequest( () request.get(/api/students/page, { params: queryParams.value }), { defaultParams: [queryParams.value], pagination: { currentKey: page, pageSizeKey: size, totalKey: data.total } } ) // 搜索 function handleSearch() { queryParams.value.page 1 run() } // 分页 function handlePageChange(page: number) { queryParams.value.page page run() } // 删除 async function handleDelete(id: number) { await ElMessageBox.confirm(确认删除, 提示, { type: warning }) await request.delete(/api/students/${id}) ElMessage.success(删除成功) run() } // 对话框 const dialogVisible ref(false) const currentStudent refStudent() function handleAdd() { currentStudent.value undefined dialogVisible.value true } function handleEdit(row: Student) { currentStudent.value { ...row } dialogVisible.value true } function handleFormSuccess() { run() } /script template div classstudent-manager !-- 搜索栏 -- div classsearch-bar el-input v-modelqueryParams.name placeholder姓名 clearable stylewidth: 150px / el-select v-modelqueryParams.sex placeholder性别 clearable stylewidth: 100px el-option label男 value男 / el-option label女 value女 / /el-select el-button typeprimary clickhandleSearch搜索/el-button el-button typeprimary clickhandleAdd新增/el-button /div !-- 表格 -- el-table :datadata?.list || [] border stripe v-loadingloading el-table-column propid label编号 width100 / el-table-column propname label姓名 / el-table-column propsex label性别 / el-table-column propage label年龄 / el-table-column label操作 width150 template #default{ row } el-button typeprimary sizesmall clickhandleEdit(row)编辑/el-button el-button typedanger sizesmall clickhandleDelete(row.id)删除/el-button /template /el-table-column /el-table !-- 分页 -- el-pagination v-model:current-pagequeryParams.page v-model:page-sizequeryParams.size :totaltotal :page-sizes[10, 20, 50] layouttotal, sizes, prev, pager, next current-changehandlePageChange / !-- 新增/编辑对话框 -- StudentForm v-modeldialogVisible :studentcurrentStudent successhandleFormSuccess / /div /template style scoped .student-manager { padding: 20px; } .search-bar { margin-bottom: 16px; display: flex; gap: 8px; } .el-pagination { margin-top: 16px; justify-content: flex-end; } /style总结本文通过学生管理案例系统讲解了 Element Plus 的核心组件组件作用关键点el-table表格展示自定义列用#default{ row }插槽el-pagination分页导航后端分页配合:total和current-changeel-dialog对话框双向绑定v-model控制显示el-form表单校验规则:rules手动校验validate()ElMessage消息提示成功/警告/错误提示ElMessageBox确认框删除等操作前确认最佳实践复杂表单抽取为子组件通过defineProps/defineEmits通信表单校验规则集中定义类似 Java 的 validation 注解分页由后端实现前端只传递参数下一步掌握了单页面开发后我们需要学习如何管理多个页面。下一篇文章将讲解 vue-router 路由管理。作者小张 | 转载请注明出处

本月热点