Element Plus
Element Plus 是基于 Vue 3 的桌面端组件库,覆盖表单、表格、弹窗、导航、反馈等常用组件。本文整理后台管理系统中使用频率最高的组件,附完整代码示例,可直接复制使用。
一、快速开始
1.1 安装
npm install element-plus
1.2 引入方式
方式一:全局引入(简单项目)
// main.js
import { createApp } from 'vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import App from './App.vue';
const app = createApp(App);
app.use(ElementPlus);
app.mount('#app');
方式二:按需引入(推荐,减小打包体积)
// 使用 unplugin-element-plus 自动导入(推荐)
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import ElementPlus from 'unplugin-element-plus/vite';
export default defineConfig({
plugins: [vue(), ElementPlus()],
});
// 或在需要的组件中手动引入
import { ElButton, ElInput } from 'element-plus';
import 'element-plus/theme-chalk/el-button.css';
import 'element-plus/theme-chalk/el-input.css';
方式三:使用 unplugin-vue-components(最推荐)
// vite.config.js
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
export default defineConfig({
plugins: [
AutoImport({ resolvers: [ElementPlusResolver()] }),
Components({ resolvers: [ElementPlusResolver()] }),
],
});
// 组件和 API 自动导入,无需手动 import!直接使用 <el-button> 和 ElMessage 等
1.3 图标使用
npm install @element-plus/icons-vue
<template>
<el-icon :size="20" color="#409EFF"><Edit /></el-icon>
<el-button type="primary"><el-icon style="margin-right:6px"><Search /></el-icon>搜索</el-button>
</template>
<script setup>
import { Edit, Search, Delete, Plus, Download, Upload } from '@element-plus/icons-vue';
</script>
二、表单组件(最核心)
2.1 基础表单 + 校验
<template>
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px" size="default">
<!-- 输入框 -->
<el-form-item label="用户名" prop="username">
<el-input v-model="form.username" placeholder="请输入用户名" clearable />
</el-form-item>
<!-- 密码 -->
<el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password" show-password placeholder="请输入密码" />
</el-form-item>
<!-- 下拉选择 -->
<el-form-item label="角色" prop="role">
<el-select v-model="form.role" placeholder="请选择角色" filterable clearable>
<el-option label="管理员" value="admin" />
<el-option label="普通用户" value="user" />
<el-option label="访客" value="guest" />
</el-select>
</el-form-item>
<!-- 日期选择 -->
<el-form-item label="创建日期" prop="date">
<el-date-picker v-model="form.date" type="date" placeholder="选择日期"
value-format="YYYY-MM-DD" />
</el-form-item>
<!-- 单选 -->
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="form.gender">
<el-radio value="male">男</el-radio>
<el-radio value="female">女</el-radio>
</el-radio-group>
</el-form-item>
<!-- 多选 -->
<el-form-item label="爱好" prop="hobbies">
<el-checkbox-group v-model="form.hobbies">
<el-checkbox label="reading">阅读</el-checkbox>
<el-checkbox label="coding">编程</el-checkbox>
<el-checkbox label="sports">运动</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- 开关 -->
<el-form-item label="状态" prop="active">
<el-switch v-model="form.active" active-text="启用" inactive-text="禁用" />
</el-form-item>
<!-- 数字输入 -->
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="0" :max="999" :step="10" />
</el-form-item>
<!-- 文本域 -->
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="3" maxlength="200" show-word-limit />
</el-form-item>
<!-- 操作按钮 -->
<el-form-item>
<el-button type="primary" @click="handleSubmit(formRef)">提交</el-button>
<el-button @click="handleReset(formRef)">重置</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from 'vue';
const formRef = ref(null);
const form = reactive({
username: '',
password: '',
role: '',
date: '',
gender: 'male',
hobbies: [],
active: true,
sort: 0,
remark: '',
});
// 校验规则
const rules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码至少 6 位', trigger: 'blur' },
],
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
date: [{ required: true, message: '请选择日期', trigger: 'change' }],
hobbies: [{ type: 'array', required: true, message: '请选择爱好', trigger: 'change' }],
};
// 自定义校验器示例
const validatePassword = (rule, value, callback) => {
if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(value)) {
callback(new Error('密码需包含大小写字母和数字'));
}
callback();
};
const handleSubmit = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
console.log('提交数据:', form);
// 调用 API
} else {
console.log('校验失败:', fields);
}
});
};
const handleReset = (formEl) => {
if (!formEl) return;
formEl.resetFields();
};
</script>
2.2 上传组件
<template>
<!-- 图片上传 -->
<el-upload
action="/api/upload"
list-type="picture-card"
:file-list="fileList"
:before-upload="beforeUpload"
:on-success="onSuccess"
:on-error="onError"
:limit="3"
:on-exceed="() => ElMessage.warning('最多上传 3 张')">
<el-icon><Plus /></el-icon>
</el-upload>
<!-- 拖拽上传 -->
<el-upload drag action="/api/upload" multiple :on-success="onSuccess">
<el-icon class="el-icon--upload"><UploadFilled /></el-icon>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
</el-upload>
</template>
<script setup>
import { ElMessage } from 'element-plus';
const beforeUpload = (file) => {
const isImage = file.type.startsWith('image/');
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isImage) { ElMessage.error('只能上传图片文件!'); return false; }
if (!isLt2M) { ElMessage.error('图片大小不能超过 2MB!'); return false; }
return true;
};
const onSuccess = (response) => {
ElMessage.success('上传成功');
};
const onError = () => {
ElMessage.error('上传失败');
};
</script>
2.3 表单组件速查
|
组件 |
标签 |
关键属性 |
|---|---|---|
|
输入框 |
|
|
|
下拉选择 |
|
|
|
单选 |
|
|
|
多选 |
|
|
|
日期选择 |
|
|
|
时间选择 |
|
|
|
数字输入 |
|
|
|
开关 |
|
|
|
滑块 |
|
|
|
评分 |
|
|
|
颜色选择器 |
|
|
|
级联选择器 |
|
|
|
穿梭框 |
|
|
|
自动完成 |
|
|
|
上传 |
|
|
三、表格组件
3.1 基础表格 + 分页 + 操作列
<template>
<!-- 搜索栏 -->
<el-form :model="query" inline>
<el-form-item label="关键词">
<el-input v-model="query.keyword" placeholder="搜索" clearable @clear="fetchData" />
</el-form-item>
<el-form-item label="状态">
<el-select v-model="query.status" placeholder="状态" clearable @change="fetchData">
<el-option label="启用" :value="1" />
<el-option label="禁用" :value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="fetchData">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<!-- 表格 -->
<el-table :data="tableData" border stripe v-loading="loading" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column type="index" label="#" width="60" />
<el-table-column prop="name" label="名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
{{ row.status === 1 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="80" align="center" />
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button size="small" @click="handleEdit(row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.size"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
background
@size-change="fetchData"
@current-change="fetchData" />
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
const loading = ref(false);
const tableData = ref([]);
const selectedRows = ref([]);
const query = reactive({ keyword: '', status: '' });
const pagination = reactive({ page: 1, size: 10, total: 0 });
const fetchData = async () => {
loading.value = true;
try {
const { data } = await api.getList({
...query,
page: pagination.page,
size: pagination.size,
});
tableData.value = data.list;
pagination.total = data.total;
} finally {
loading.value = false;
}
};
const handleSelectionChange = (rows) => {
selectedRows.value = rows;
};
const handleDelete = (row) => {
ElMessageBox.confirm('确认删除该项?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(async () => {
await api.delete(row.id);
ElMessage.success('删除成功');
fetchData();
}).catch(() => {});
};
const handleReset = () => {
query.keyword = '';
query.status = '';
fetchData();
};
onMounted(() => fetchData());
</script>
3.2 表格进阶特性
自定义列内容(插槽 + 操作)
<el-table :data="data">
<!-- 图片列 -->
<el-table-column label="头像" width="80">
<template #default="{ row }">
<el-avatar :size="40" :src="row.avatar" />
</template>
</el-table-column>
<!-- 格式化列 -->
<el-table-column label="金额" width="120" align="right">
<template #default="{ row }">
¥{{ row.amount.toFixed(2) }}
</template>
</el-table-column>
<!-- 条件渲染列 -->
<el-table-column label="状态" width="100">
<template #default="{ row }">
<el-tag :type="statusMap[row.status]?.type" effect="plain">
{{ statusMap[row.status]?.text }}
</el-tag>
</template>
</el-table-column>
</el-table>
<script setup>
const statusMap = {
0: { text: '待审核', type: 'warning' },
1: { text: '已通过', type: 'success' },
2: { text: '已拒绝', type: 'danger' },
};
</script>
表格常用属性速查
|
属性 |
说明 |
示例值 |
|---|---|---|
|
|
表格数据 |
|
|
|
竖向边框 |
|
|
|
斑马纹 |
|
|
|
加载中 |
|
|
|
尺寸 |
|
|
|
列宽度自撑开 |
|
|
|
显示表头 |
|
|
|
高亮当前行 |
|
|
|
行数据的 Key |
|
|
|
树形数据配置 |
|
|
|
空数据文案 |
|
|
|
最大高度(超出固定表头滚动) |
|
|
|
超出省略 + tooltip |
|
|
|
固定列 |
|
el-pagination 分页属性
|
属性 |
说明 |
示例 |
|---|---|---|
|
|
当前页 |
|
|
|
每页条数 |
|
|
|
总条数 |
|
|
|
每页条数选项 |
|
|
|
布局 |
|
|
|
背景色 |
|
|
|
小型分页 |
|
四、弹窗与对话框
4.1 Dialog(对话框 / 模态框)
<template>
<!-- 触发按钮 -->
<el-button type="primary" @click="dialogVisible = true">打开对话框</el-button>
<!-- 对话框 -->
<el-dialog
v-model="dialogVisible"
:title="isEdit ? '编辑' : '新增'"
width="600px"
:close-on-click-modal="false"
@closed="handleClosed">
<!-- 对话框内容:表单 -->
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" />
</el-form-item>
<!-- 更多表单项... -->
</el-form>
<!-- 底部按钮 -->
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitting" @click="handleConfirm">确定</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive, nextTick } from 'vue';
const dialogVisible = ref(false);
const submitting = ref(false);
const isEdit = ref(false);
const formRef = ref(null);
const form = reactive({ name: '' });
// 打开新增
const handleAdd = () => {
isEdit.value = false;
dialogVisible.value = true;
// 如果用 resetFields,需等 DOM 更新
nextTick(() => formRef.value?.resetFields());
};
// 打开编辑
const handleEdit = (row) => {
isEdit.value = true;
dialogVisible.value = true;
nextTick(() => {
Object.assign(form, row);
});
};
// 确定
const handleConfirm = async () => {
await formRef.value.validate();
submitting.value = true;
try {
isEdit.value ? await api.update(form) : await api.create(form);
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功');
dialogVisible.value = false;
emit('success');
} finally {
submitting.value = false;
}
};
// 关闭后清理
const handleClosed = () => {
formRef.value?.resetFields();
};
</script>
Dialog 常用属性速查
|
属性 |
说明 |
默认值 |
|---|---|---|
|
|
显示/隐藏 |
|
|
|
标题 |
|
|
|
宽度 |
|
|
|
全屏 |
|
|
|
距顶部距离 |
|
|
|
显示遮罩层 |
|
|
|
点击遮罩关闭 |
|
|
|
按 ESC 关闭 |
|
|
|
显示关闭按钮 |
|
|
|
可拖拽 |
|
|
|
关闭时销毁内容 |
|
|
|
打开/关闭事件 |
- |
4.2 Drawer(抽屉)
<template>
<el-button @click="drawerVisible = true">打开抽屉</el-button>
<el-drawer
v-model="drawerVisible"
title="详情"
direction="rtl" <!-- rtl(右)/ltr(左)/ttb(上)/btt(下) -->
size="40%">
<p>抽屉内容...</p>
</el-drawer>
</template>
4.3 Popover / Tooltip(气泡提示)
<!-- Popover:点击触发,内容丰富 -->
<el-popover placement="bottom" :width="300" trigger="click">
<template #reference>
<el-button>点击弹出</el-button>
</template>
<p>可以放表格、表单等任何内容</p>
</el-popover>
<!-- Tooltip:hover 触发,纯文本 -->
<el-tooltip content="这是提示文字" placement="top">
<el-button>悬停查看</el-button>
</el-tooltip>
五、消息提示与反馈
5.1 Message(消息提示)
import { ElMessage } from 'element-plus';
// 四种类型
ElMessage.success('操作成功');
ElMessage.warning('警告信息');
ElMessage.error('操作失败');
ElMessage.info('普通信息');
// 配置选项
ElMessage({
type: 'success',
message: '这是一条不会自动关闭的消息',
duration: 0, // 0 = 不自动关闭
showClose: true, // 显示关闭按钮
center: true, // 文字居中
offset: 100, // 距顶偏移
// 自定义关闭回调
onClose: () => { console.log('消息关闭了'); }
});
5.2 MessageBox(确认框)
import { ElMessageBox } from 'element-plus';
// 确认框
ElMessageBox.confirm('确定删除该记录?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
// 点击确定
}).catch(() => {
// 点击取消(catch 是取消,不是异常!)
});
// 提示框
ElMessageBox.alert('操作不可逆!', '注意', { confirmButtonText: '知道了' });
// 输入框
ElMessageBox.prompt('请输入备注', '提示', {
inputValidator: (val) => val ? true : '不能为空'
}).then(({ value }) => {
console.log('输入内容:', value);
});
// 自定义内容
ElMessageBox({
title: '确认提交',
message: h('p', null, [
h('span', null, '内容可以是 '),
h('span', { style: 'color: red' }, 'VNode'),
]),
showCancelButton: true,
});
5.3 Notification(通知)
import { ElNotification } from 'element-plus';
ElNotification({
title: '新消息',
message: '您有一条新的系统通知',
type: 'success', // success / warning / info / error
duration: 4500,
position: 'top-right', // top-right / top-left / bottom-right / bottom-left
});
5.4 Loading(加载)
import { ElLoading } from 'element-plus';
// 全屏 loading
const loading = ElLoading.service({ text: '加载中...', fullscreen: true });
await doSomething();
loading.close();
// 局部 loading(配合 el-table 的 v-loading 更快)
// <el-table v-loading="loading" ...>
六、布局与容器
6.1 Container 布局容器(后台管理经典布局)
<template>
<el-container style="height: 100vh">
<!-- 左侧菜单 -->
<el-aside width="200px">
<el-menu :default-active="activeMenu" router background-color="#304156" text-color="#bfcbd9" active-text-color="#409EFF">
<el-menu-item index="/dashboard">
<el-icon><HomeFilled /></el-icon>
<span>首页</span>
</el-menu-item>
<el-sub-menu index="system">
<template #title>
<el-icon><Setting /></el-icon>
<span>系统管理</span>
</template>
<el-menu-item index="/system/user">用户管理</el-menu-item>
<el-menu-item index="/system/role">角色管理</el-menu-item>
</el-sub-menu>
</el-menu>
</el-aside>
<!-- 右侧主体 -->
<el-container>
<!-- 顶部导航 -->
<el-header height="60px" style="border-bottom: 1px solid #eee">
<div class="header-content">
<span>后台管理系统</span>
<el-dropdown>
<span class="user-info">管理员 ▼</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>个人信息</el-dropdown-item>
<el-dropdown-item divided @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<!-- 内容区 -->
<el-main>
<router-view />
</el-main>
</el-container>
</el-container>
</template>
6.2 Tabs 标签页
<el-tabs v-model="activeTab" type="border-card" @tab-click="onTabClick">
<el-tab-pane label="基本信息" name="basic">基本信息内容</el-tab-pane>
<el-tab-pane label="高级设置" name="advanced">高级设置内容</el-tab-pane>
</el-tabs>
6.3 栅格布局(Row / Col)
<el-row :gutter="20">
<el-col :span="6">
<!-- 占 1/4 -->
<el-card>统计卡片</el-card>
</el-col>
<el-col :span="6"><el-card>统计卡片</el-card></el-col>
<el-col :span="12">
<!-- 占 1/2 -->
<el-card>图表</el-card>
</el-col>
</el-row>
响应式断点:xs(<768) / sm(≥768) / md(≥992) / lg(≥1200) / xl(≥1920)
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<!-- 手机全宽 → 平板半宽 → 桌面1/4 -->
</el-col>
七、常用组件速查与完整示例
7.1 按钮 el-button
<!-- 类型 -->
<el-button>默认</el-button>
<el-button type="primary">主要</el-button>
<el-button type="success">成功</el-button>
<el-button type="warning">警告</el-button>
<el-button type="danger">危险</el-button>
<el-button type="info">信息</el-button>
<!-- 状态 -->
<el-button :loading="true">加载中</el-button>
<el-button :disabled="true">禁用</el-button>
<!-- 样式 -->
<el-button plain>朴素</el-button>
<el-button round>圆角</el-button>
<el-button circle><el-icon><Search /></el-icon></el-button>
<el-button size="large">大</el-button>
<el-button size="small">小</el-button>
<!-- 图标 -->
<el-button type="primary"><el-icon style="margin-right:6px"><Plus /></el-icon>新增</el-button>
7.2 标签 el-tag
<el-tag>默认</el-tag>
<el-tag type="success">完成</el-tag>
<el-tag type="warning">待处理</el-tag>
<el-tag type="danger">失败</el-tag>
<el-tag type="info">已取消</el-tag>
<!-- 可关闭 -->
<el-tag closable @close="handleClose">可关闭标签</el-tag>
<!-- 效果 -->
<el-tag effect="dark">深色</el-tag>
<el-tag effect="plain">浅色</el-tag>
<!-- 圆形 -->
<el-tag round>圆角</el-tag>
<!-- 尺寸 -->
<el-tag size="large">大号</el-tag>
7.3 进度条 el-progress
<el-progress :percentage="70" />
<el-progress :percentage="100" status="success" />
<el-progress :percentage="50" status="exception" />
<el-progress :percentage="30" :stroke-width="20" :text-inside="true" />
<!-- 环形 -->
<el-progress type="circle" :percentage="80" :width="120" />
<!-- 仪表盘 -->
<el-progress type="dashboard" :percentage="75" />
7.4 头像 el-avatar
<el-avatar :size="50" src="https://..." />
<el-avatar :size="40" @error="errorHandler">
<img src="fallback.jpg" />
</el-avatar>
<!-- 文字头像 -->
<el-avatar>Admin</el-avatar>
7.5 卡片 el-card
<el-card shadow="hover">
<template #header>
<span>卡片标题</span>
</template>
<p>卡片内容</p>
</el-card>
7.6 折叠面板 el-collapse
<el-collapse v-model="activeNames" accordion>
<el-collapse-item title="一致性" name="1">
<p>与现实生活一致:与现实生活的流程、逻辑保持一致,遵循用户习惯的语言和概念</p>
</el-collapse-item>
<el-collapse-item title="反馈" name="2">
<p>控制反馈:通过界面样式和交互动效让用户可以清晰的感知自己的操作</p>
</el-collapse-item>
</el-collapse>
7.7 树形控件 el-tree
<template>
<el-tree
:data="treeData"
node-key="id"
:props="{ children: 'children', label: 'name' }"
default-expand-all
show-checkbox
check-strictly
highlight-current
@node-click="onNodeClick"
@check="onCheck" />
</template>
<script setup>
const treeData = [
{
id: 1, name: '一级菜单',
children: [
{ id: 11, name: '子菜单 1' },
{ id: 12, name: '子菜单 2' },
]
}
];
// 获取选中节点
const getCheckedKeys = () => treeRef.value?.getCheckedKeys();
const getCheckedNodes = () => treeRef.value?.getCheckedNodes();
const setCheckedKeys = (keys) => treeRef.value?.setCheckedKeys(keys);
// 过滤/筛选节点
const filterNode = (value, data) => data.name.includes(value);
// <el-tree :filter-node-method="filterNode" ref="treeRef" ... />
</script>
7.8 步骤条 el-steps
<el-steps :active="step" align-center finish-status="success">
<el-step title="步骤 1" description="填写信息" />
<el-step title="步骤 2" description="验证信息" />
<el-step title="步骤 3" description="完成" />
</el-steps>
7.9 时间线 el-timeline
<el-timeline>
<el-timeline-item timestamp="2024-01-01" placement="top">
<el-card><p>创建项目</p></el-card>
</el-timeline-item>
<el-timeline-item timestamp="2024-01-15" placement="top">
<el-card><p>发布 v1.0</p></el-card>
</el-timeline-item>
<el-timeline-item timestamp="2024-02-01" placement="top" type="success">
<el-card><p>完成验收</p></el-card>
</el-timeline-item>
</el-timeline>
7.10 走马灯 el-carousel
<el-carousel :interval="4000" type="card" height="200px">
<el-carousel-item v-for="i in 4" :key="i">
<h3>{{ i }}</h3>
</el-carousel-item>
</el-carousel>
7.11 描述列表 el-descriptions
<el-descriptions :column="2" border>
<el-descriptions-item label="用户名" :span="1">admin</el-descriptions-item>
<el-descriptions-item label="手机号">13800138000</el-descriptions-item>
<el-descriptions-item label="角色" :span="2">超级管理员</el-descriptions-item>
<el-descriptions-item label="备注">
<el-tag size="small">学校</el-tag>
</el-descriptions-item>
</el-descriptions>
7.12 空状态 el-empty
<el-empty description="暂无数据" :image-size="200" />
<!-- 自定义图片 -->
<el-empty description="暂无订单">
<el-button type="primary">去下单</el-button>
</el-empty>
7.13 骨架屏 el-skeleton
<el-skeleton :rows="5" animated :loading="loading">
<template #default>
<p>实际内容...</p>
</template>
</el-skeleton>
<!-- 详细骨架 -->
<el-skeleton>
<template #template>
<el-skeleton-item variant="image" style="width: 240px; height: 240px" />
<div style="padding: 14px">
<el-skeleton-item variant="h3" />
<el-skeleton-item variant="text" style="margin-top: 16px" />
</div>
</template>
</el-skeleton>
7.14 回到顶部 el-backtop
<el-backtop :bottom="100" :visibility-height="300" />
八、后台管理常用组合套路
8.1 增删改查页面模板
<template>
<div class="page-container">
<!-- 1. 搜索区域 -->
<el-card class="search-card">
<el-form :model="query" inline>
<el-form-item label="关键词"><el-input v-model="query.keyword" clearable placeholder="请输入" /></el-form-item>
<el-form-item label="状态">
<el-select v-model="query.status" clearable placeholder="请选择">
<el-option label="启用" :value="1" />
<el-option label="禁用" :value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search">搜索</el-button>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 2. 操作栏 -->
<el-card class="tool-card">
<el-button type="primary" @click="handleAdd"><el-icon><Plus /></el-icon>新增</el-button>
<el-button type="danger" :disabled="!selected.length" @click="batchDelete">批量删除</el-button>
<el-button @click="exportData"><el-icon><Download /></el-icon>导出</el-button>
</el-card>
<!-- 3. 数据表格 -->
<el-card class="table-card">
<el-table :data="tableData" border stripe v-loading="loading"
@selection-change="(rows) => selected = rows">
<el-table-column type="selection" width="50" />
<el-table-column type="index" label="#" width="60" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
{{ row.status === 1 ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }">
<el-button size="small" @click="handleEdit(row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination v-model:current-page="pagination.page" v-model:page-size="pagination.size"
:total="pagination.total" :page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next" background />
</el-card>
<!-- 4. 新增/编辑弹窗 -->
<el-dialog v-model="dialogVisible" :title="isEdit ? '编辑' : '新增'" width="600px" @closed="onClosed">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitting" @click="submit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
8.2 全局确认删除 Hook(封装复用)
// composables/useDelete.js
import { ElMessage, ElMessageBox } from 'element-plus';
export function useDelete(api, options = {}) {
const { message = '确认删除?', successMsg = '删除成功' } = options;
const confirm = (idOrRow, onSuccess) => {
ElMessageBox.confirm(message, '提示', {
type: 'warning',
confirmButtonText: '确定',
}).then(async () => {
const id = typeof idOrRow === 'object' ? idOrRow.id : idOrRow;
await api(id);
ElMessage.success(successMsg);
onSuccess?.();
}).catch(() => {}); // 取消操作
};
return { confirm };
}
// 使用
const { confirm: deleteUser } = useDelete(api.deleteUser);
deleteUser(row, () => fetchData());
附录:后台开发最常用 Top 8
|
排名 |
组件 |
使用频率 |
关键场景 |
|---|---|---|---|
|
1 |
el-form + el-input + 校验 |
100% |
所有新增/编辑/搜索 |
|
2 |
el-table + el-pagination |
100% |
所有列表页 |
|
3 |
el-dialog |
90% |
新增/编辑弹窗 |
|
4 |
ElMessage / ElMessageBox |
90% |
操作反馈 |
|
5 |
el-select + el-option |
85% |
下拉选择/筛选 |
|
6 |
el-date-picker |
80% |
日期筛选/选择 |
|
7 |
el-button |
100% |
所有操作入口 |
|
8 |
el-container + el-menu |
70% |
布局框架 |
学习建议:先精通 Top 5(表单、表格、弹窗、消息提示、下拉选择),再逐一覆盖其他组件。每个组件优先看官方文档的 Demo,再对照本文的完整示例理解实际业务场景中的用法。