====最后一次课了,给个关注和赞呗
🌲 简介
Element Plus 是一个基于 Vue 3 的高质量 UI 组件库。它包含了丰富的组件和扩展功能,例如表格、表单、按钮、导航、通知等,让开发者能够快速构建高质量的 Web 应用。Element Plus 的设计理念是:提供开箱即用的 UI 组件和扩展功能,帮助开发者快速构建应用程序,同时提供详细的文档和教程,让开发者更好地掌握和使用 Element Plus。
🌲 安装Element-plus
🌿 安装element-plus
npm install element-plus
🌿 完整引用
如果你对打包后的文件大小不是很在乎,那么可以将element-plus所有内容全部导入到项目中(不建议)
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-vue-components 和 unplugin-auto-import 两款插件
npm install -D unplugin-vue-components unplugin-auto-import
然后修改vite.config.js配置文件,在plugins插件中添加AutoImport和Components相关配置
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers:[ElementPlusResolver()],
}),
Components({
resolvers:[ElementPlusResolver()],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
最后可以直接在组件中使用了
<script setup>
</script>
<template>
<el-button type="primary">Primary</el-button>
</template>
<style scoped>
</style>
🌲 加载Element-plus字体图标
Element 不仅仅是提供了各种组件,同时还提供了一整套的字体图标方便开发者使用。
🌿 安装icons字体图标
npm install @element-plus/icons-vue
🌿 全局注册
在根目录下,创建plugins文件夹,在文件夹下创建icons.js文件
import * as components from "@element-plus/icons-vue"
export default{
install:(app)=>{
for(const key in components){
const componentConfig = components[key];
app.component(componentConfig.name,componentConfig);
}
}
}
🌿 引入文件
在main.js引入icons.js
import { createApp } from 'vue'
// import ElementPlus from 'element-plus'
// import 'element-plus/dist/index.css'
import App from './App.vue'
import elementIcon from './plugins/icons'
const app = createApp(App)
// app.use(ElementPlus)
app.use(elementIcon)
app.mount('#app')
🌿 使用方式
接下来,就可以直接在组件中引入使用了
<template>
<el-button type="primary">Primary</el-button>
<el-icon class="expand" color="#409efc">
<expand/>
</el-icon>
</template>
展示效果:
🌲 表单:登录页面
<template>
<div>
</div>
<el-form :model="form" style="width:500px" >
<el-text class="mx-1" type="primary" >
<h2 style="text-align: center;">用户登录</h2>
</el-text>
<el-form-item label="账 号">
<el-input v-model="form.username" />
</el-form-item>
<el-form-item label="密 码">
<!-- 使用 show-password 属性即可得到一个可切换显示隐藏的密码框 -->
<el-input v-model="form.password" type="password" show-password/>
</el-form-item>
<el-form-item label-width="45px">
<!-- 设置按钮大小时,需要先设置size,然后再设置style中的width和height -->
<el-button type="primary" @click="onSubmit()" size="large" style="width: 100px;">登录</el-button>
<el-button type="danger" @click="onReset()" size="large" style="width: 100px;">取消</el-button>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
// do not use same name with ref
const form = reactive({
username: '',
password: ''
})
const onSubmit = () => {
console.log('submit!')
}
const onReset = ()=>{
form.username = ""
form.password = ""
}
</script>
🌲 列表:英雄列表
🌿 页面内容
<template>
<!-- :data 配置查询到的列表数据源 -->
<el-table
ref="multipleTableRef"
:data="tableData"
style="width: 100%"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="序号" width="120">
</el-table-column>
<!-- -->
<el-table-column property="hname" label="英雄名称" />
<el-table-column property="job" label="英雄职业" />
<el-table-column property="sex" label="英雄性别" />
<el-table-column property="level" label="英雄技能" />
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">修改</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 20px">
<el-button @click="toggleSelection([tableData[1], tableData[2]])">Toggle selection status of second and third rows</el-button>
<el-button @click="toggleSelection()">Clear selection</el-button>
</div>
</template>
<script setup>
</script>
- 从scope中可以获取当前行的数据
🌿 异步请求数据
<script setup>
import {ref,computed,reactive} from 'vue'
import axios from 'axios'
import qs from 'querystring'
import router from '../router';
// 此处的tableData是上面模板中table里:data所对应的名称
const tableData = ref([{}]);// 表格数据实际上是一个对象数组
const load_list = ()=>{
// 异步请求列表数据
axios.get("/api/hero").then(res=>{
console.log(res.data);
tableData.value = res.data.data;
});
}
load_list()
</script>
此处get请求对应的请求地址为之前我们使用SpringBoot编写好的请求。
🌿 修改数据
<!-- 修改数据对话框 -->
<el-dialog v-model="dialogFormVisible" title="修改英雄数据">
<el-form :model="form">
<el-form-item label="英雄名称" :label-width="formLabelWidth">
<el-input v-model="form.hname" autocomplete="off" />
</el-form-item>
<el-form-item label="英雄职业" :label-width="formLabelWidth">
<el-input v-model="form.job" autocomplete="off" />
</el-form-item>
<el-form-item label="英雄性别" :label-width="formLabelWidth">
<el-radio-group v-model="form.sex">
<el-radio label="男" />
<el-radio label="女" />
</el-radio-group>
</el-form-item>
<el-form-item label="英雄等级" :label-width="formLabelWidth">
<el-input v-model="form.level" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">Cancel</el-button>
<el-button type="primary" @click="dialogFormVisible = false">
Confirm
</el-button>
</span>
</template>
</el-dialog>
这个对话框是通过一个"dialogFormVisible"响应式属性来控制的,在上面点击修改时,可以将这个值设置为true。
而:<el-form :model="form">中的这个响应式属性值form是用来绑定表单的属性值的。
基本逻辑是,点击修改时,将对话框的dialogFormVisible的value值设置为true,并且将从表格中获取到的行数据给到form的value。
const dialogFormVisible = ref(false); // 弹出对话框
const formLabelWidth = '140px';
const form = ref({}) // 定义表单中的数据
const handleEdit = (row)=>{
console.log(row.id)
// 显示对话框
dialogFormVisible.value = true;
form.value = row
}
实现提交修改按钮中的方法updateHero方法:
const updateHero = ()=>{
axios.put('/api/hero',
qs.stringify(form.value))
.then(res=>{
if(res.data.code=='0000'){
dialogFormVisible.value = false;
}else{
alert("系统繁忙...请稍后重试")
}
});
}
🌿 删除数据
- 实现删除按钮中的handleDelete方法,打开确认删除对话框
//-----------------------------------------------------
// 删除数据提醒框
const centerDialogVisible = ref(false);
const handleDelete=(row)=>{
centerDialogVisible.value = true
form.value = row
}
- 删除数据提醒框:
<!-- 删除数据提醒框 -->
<el-dialog v-model="centerDialogVisible" title="提示信息" width="30%" center>
<div align="center">
是否确定删除此条数据?
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="centerDialogVisible = false">取消</el-button>
<el-button type="primary" @click="deleteHero(form.id)">
删除
</el-button>
</span>
</template>
</el-dialog>
- deleteHero删除数据的方法
// 删除数据
const deleteHero=(id)=>{
axios.delete(`/api/hero/${id}`).then(res=>{
if(res.data.code=='0000'){
centerDialogVisible.value = false
load_list()
}else{
alert("系统繁忙...请稍后重试")
}
})
}
🌿 新增数据
- 新增数据的对话框
<!-- 新增数据对话框 -->
<el-dialog v-model="dialogAddHero" title="添加英雄数据">
<el-form :model="hero">
<el-form-item label="英雄名称" :label-width="formLabelWidth">
<el-input v-model="hero.hname" autocomplete="off" />
</el-form-item>
<el-form-item label="英雄职业" :label-width="formLabelWidth">
<el-input v-model="hero.job" autocomplete="off" />
</el-form-item>
<el-form-item label="英雄性别" :label-width="formLabelWidth">
<el-radio-group v-model="hero.sex">
<el-radio label="男" />
<el-radio label="女" />
</el-radio-group>
</el-form-item>
<el-form-item label="英雄等级" :label-width="formLabelWidth">
<el-input v-model="hero.level" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="addHeroData(hero)">
确定
</el-button>
</span>
</template>
</el-dialog>
- js代码如下
//-----------------------------------------------
// 打开添加英雄数据对话框
const dialogAddHero = ref(false)
const hero = reactive({})
const isDark = ref(true)
const openAdd = ()=>{
dialogAddHero.value = true
}
const addHeroData= (hero)=>{
axios.post("/api/hero",qs.stringify(hero)).then(res=>{
if(res.data.code=="0000"){
dialogAddHero.value = false;
alert("添加成功");
load_list()
}else{
alert("系统繁忙...请稍后重试")
}
})
}
🌿 分页查询
- 添加分页插件
<!-- 分页 -->
<div>
<el-pagination
style="margin: 30px 180px;"
:background = 'true'
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[5, 15, 20, 50,100]"
:small="true"
:disabled="false"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
- 添加更改当前页数,以及每页显示总条目数的方法
const pageSize = ref(5)
const currentPage = ref(1)
const total = ref(0)
const handleSizeChange = (val)=>{
pageSize.value = val
load_list();
}
const handleCurrentChange = (val) => {
currentPage.value = val
load_list()
}
- 修改查询异步请求地址
const load_list = ()=>{
// 异步请求列表数据
axios.get(`/api/hero/${currentPage.value}/${pageSize.value}`,).then(res=>{
console.log("..."+res.data.data);
tableData.value = res.data.data.list;
total.value = res.data.data.total
});
}
load_list()
- 修改后端添加分页插件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
- 添加分页插件配置
# 数据库方言
pagehelper.helper-dialect=mysql
# 分页合理化
pagehelper.reasonable=true
# 是否支持接口参数来传递分页参数,默认为false
pagehelper.support-methods-arguments=true
# 当设置为true的时候,如果pagesize设置为0(或者RowBounds的limit=0),就不执行分页,查询所有
pagehelper.page-size-zero=true
pagehelper.params=count=countSql
- 修改接口方法,接收传过来的分页参数
@Autowired
private IHeroService service;
@GetMapping("/hero/{currentPage}/{pageSize}")
public Result getHeros(@PathVariable int currentPage,@PathVariable int pageSize){
log.info("查询所有的用户");
return service.getHeros(currentPage,pageSize);
}
- 修改Service方法,返回pageInfo对象
public Result getHeros(int currentPage, int pageSize) {
currentPage= currentPage ==0?1:currentPage;
pageSize= pageSize==0?5:pageSize;
// 设置分页起始页以及每页显示的条目总数量
PageHelper.startPage(currentPage,pageSize);
List<Hero> heroList = heroMapper.selectList(null);
// 调用业务层处理业务逻辑,最终返回一个PageInfo对象,pageInfo对象包含了
// 分页数据。包括:数据源,总页数,总条目数,当前页数等等
PageInfo<Hero> pageInfo = new PageInfo<>(heroList);
return Result.sendResult(ReturnCode.SUCCESS,pageInfo);
}
🌲前端完整代码
🌿 跨域和按需引入EL-PLUS
- 解决跨域问题:vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers:[ElementPlusResolver()],
}),
Components({
resolvers:[ElementPlusResolver()],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server:{
proxy:{
// 配置代理请求前缀
// 当vue发送localhost:5173/api/xxx请求时
'/api': {
// 实际上执行的是 http://localhost:8080/xxx
target: 'http://localhost:8080/',
changeOrigin: true,
// 到达真实服务器地址时,去掉请求前缀/api
// 比如前端发送请求http://localhost:5173/api/hero
// 经过rewrite重写后会变成:http://localhost:8080/hero
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
🌿 引入EL-PLUS图标
import * as components from "@element-plus/icons-vue"
export default{
install:(app)=>{
for(const key in components){
const componentConfig = components[key];
app.component(componentConfig.name,componentConfig);
}
}
}
🌿 英雄列表页面
<template>
<br/>
<el-button color="#626aef" :dark="isDark" @click="openAdd()">+ 新增英雄</el-button>
<el-button color="#ff0000" :dark="isDark">- 批量删除</el-button>
<br/>
<br/>
<!-- :data 配置查询到的列表数据源 -->
<el-table
ref="multipleTableRef"
:data="tableData"
style="width: 100%"
text-align="center">
<el-table-column type="selection" width="55" />
<el-table-column type="index" :index="indexMethod" label="序号" width="120" align="center">
</el-table-column>
<!-- -->
<el-table-column property="hname" label="英雄名称" align="center"/>
<el-table-column property="job" label="英雄职业" align="center"/>
<el-table-column property="sex" label="英雄性别" align="center"/>
<el-table-column property="level" label="英雄技能" align="center"/>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">修改</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div>
<el-pagination
style="margin: 30px 180px;"
:background = 'true'
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[5, 15, 20, 50,100]"
:small="true"
:disabled="false"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<!-- 修改数据对话框 -->
<el-dialog v-model="dialogFormVisible" title="修改英雄数据">
<el-form :model="form">
<el-form-item label="英雄名称" :label-width="formLabelWidth">
<el-input v-model="form.hname" autocomplete="off" />
</el-form-item>
<el-form-item label="英雄职业" :label-width="formLabelWidth">
<el-input v-model="form.job" autocomplete="off" />
</el-form-item>
<el-form-item label="英雄性别" :label-width="formLabelWidth">
<el-radio-group v-model="form.sex">
<el-radio label="男" />
<el-radio label="女" />
</el-radio-group>
</el-form-item>
<el-form-item label="英雄等级" :label-width="formLabelWidth">
<el-input v-model="form.level" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="updateHero">
确定
</el-button>
</span>
</template>
</el-dialog>
<!-- 删除数据提醒框 -->
<el-dialog v-model="centerDialogVisible" title="提示信息" width="30%" center>
<div align="center">
是否确定删除此条数据?
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="centerDialogVisible = false">取消</el-button>
<el-button type="primary" @click="deleteHero(form.id)">
删除
</el-button>
</span>
</template>
</el-dialog>
<!-- 新增数据对话框 -->
<el-dialog v-model="dialogAddHero" title="添加英雄数据">
<el-form :model="hero">
<el-form-item label="英雄名称" :label-width="formLabelWidth">
<el-input v-model="hero.hname" autocomplete="off" />
</el-form-item>
<el-form-item label="英雄职业" :label-width="formLabelWidth">
<el-input v-model="hero.job" autocomplete="off" />
</el-form-item>
<el-form-item label="英雄性别" :label-width="formLabelWidth">
<el-radio-group v-model="hero.sex">
<el-radio label="男" />
<el-radio label="女" />
</el-radio-group>
</el-form-item>
<el-form-item label="英雄等级" :label-width="formLabelWidth">
<el-input v-model="hero.level" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="addHeroData(hero)">
确定
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup>
import {ref,computed,reactive} from 'vue'
import axios from 'axios'
import qs from 'querystring'
import router from '../router';
// 此处的tableData是上面模板中table里:data所对应的名称
const tableData = ref([{}]);// 表格数据实际上是一个对象数组
const pageSize = ref(5)
const currentPage = ref(1)
const total = ref(0)
const handleSizeChange = (val)=>{
pageSize.value = val
load_list();
}
const handleCurrentChange = (val) => {
currentPage.value = val
load_list()
}
const load_list = ()=>{
// 异步请求列表数据
axios.get(`/api/hero/${currentPage.value}/${pageSize.value}`,).then(res=>{
console.log("..."+res.data.data);
tableData.value = res.data.data.list;
total.value = res.data.data.total
});
}
load_list()
const indexMethod = (index)=>{
return index +1
}
//---------------------------------------------
// 修改数据对话框
const dialogFormVisible = ref(false); // 弹出对话框
const formLabelWidth = '140px';
const form = ref({}) // 定义表单中的数据
// 弹出修改数据对话框
const handleEdit = (row)=>{
// 显示对话框
dialogFormVisible.value = true;
form.value = row
}
// 修改数据
const updateHero = ()=>{
axios.put('/api/hero',
qs.stringify(form.value)).then(res=>{
if(res.data.code=='0000'){
dialogFormVisible.value = false;
}else{
alert("系统繁忙...请稍后重试")
}
});
}
//-----------------------------------------------------
// 删除数据提醒框
const centerDialogVisible = ref(false);
const handleDelete=(row)=>{
centerDialogVisible.value = true
form.value = row
}
// 删除数据
const deleteHero=(id)=>{
axios.delete(`/api/hero/${id}`).then(res=>{
if(res.data.code=='0000'){
centerDialogVisible.value = false
load_list()
}else{
alert("系统繁忙...请稍后重试")
}
})
}
//-----------------------------------------------
// 打开添加英雄数据对话框
const dialogAddHero = ref(false)
const hero = reactive({})
const isDark = ref(true)
const openAdd = ()=>{
dialogAddHero.value = true
}
const addHeroData= (hero)=>{
axios.post("/api/hero",qs.stringify(hero)).then(res=>{
if(res.data.code=="0000"){
dialogAddHero.value = false;
alert("添加成功");
load_list()
}else{
alert("系统繁忙...请稍后重试")
}
})
}
</script>
<style scoped>
.el-button--text {
margin-right: 15px;
}
.el-select {
width: 300px;
}
.el-input {
width: 300px;
}
.dialog-footer button:first-child {
margin-right: 10px;
}
</style>