前期项目链接:项目1
在项目1的基础上,引入element组件库,让前端代码变得更美观。
一.项目简介:
前端采用Vue3+Element组件库,后端采用Spring boot+mybatis框架+mysql数据库
项目后端:链接
运行结果:
前期准备:
1)配置组件库及自动导入
1.在cmd里输入yarn add element-plus --save下载组件库插件和yarn add @element-plus/icons-vue安装图标
2.main.js文件配置
import './assets/main.css'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
//添加中文支持
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.use(ElementPlus, {
locale: zhCn,
})
app.mount('#app')
3.vite.config.js文件配置
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import AutoImport from 'unplugin-auto-import/vite';//自动导入,如果想要配置还需要下面的安装命令
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
AutoImport({
imports: [
'vue',
'vue-router'
],
resolvers: [
ElementPlusResolver()
],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})
如果想要使用自动导入,不想每次调用组件和加载组件的时候导入组件,直接用可以在vite.config.js文件配置import AutoImport from 'unplugin-auto-import/vite';
接着,输入npm install -D unplugin-vue-components unplugin-auto-import命令来安装自动导入所依赖的插件。就可以了。
2)导入sql文件
我的项目就是一个简单的测试,只写了一张Admin表,就只有账号,密码和名称三个字段。
/*
Navicat Premium Data Transfer
Source Server : 8.1
Source Server Type : MySQL
Source Server Version : 80100 (8.1.0)
Source Host : localhost:3306
Source Schema : mybaitistest
Target Server Type : MySQL
Target Server Version : 80100 (8.1.0)
File Encoding : 65001
Date: 07/07/2025 10:35:02
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for admin
-- ----------------------------
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin` (
`Account` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`Password` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`Name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`Account`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
二.前端设计
1) 目录结构
2) 关键代码解析:
1.admin.js
import axios from 'axios'
const API_URL = 'http://localhost:8080/api/admins'
export default {
getAllAdmins() {
return axios.get(API_URL)
},
getAdmin(account) {
return axios.get(`${API_URL}/${account}`)
},
addAdmin(admin) {
return axios.post(API_URL, admin)
},
updateAdmin(account, admin) {
return axios.put(`${API_URL}/${account}`, admin)
},
deleteAdmin(account) {
return axios.delete(`${API_URL}/${account}`)
}
}
解析:
这里的代理地址(指向后端控制器的统一资源地址)为'http://localhost:8080/api/admins'。
后端
@GetMapping public List<Admin> getAllAdmins() { return adminMapper.findAll(); }
@GetMapping里没有设置地址,默认为根地址api/admins
@RequestMapping("/api/admins")
axios.get用来访问后端接口地址。
2.AdminManagement.vue
<template>
<div class="admin-management">
<h2>管理员管理</h2>
<!-- 添加管理员表单 -->
<el-form :model="form" label-width="120px" style="margin-bottom: 20px;">
<el-form-item label="账号">
<el-input v-model="form.account" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="form.password" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="form.name" style="width: 200px;"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="addAdmin">添加</el-button>
</el-form-item>
</el-form>
<!-- 管理员列表 -->
<el-table :data="admins" border style="width: 100%">
<el-table-column prop="account" label="账号" width="180"></el-table-column>
<el-table-column prop="name" label="姓名" width="180"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.row.account)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 编辑对话框 -->
<el-dialog v-model="dialogVisible" title="编辑管理员">
<el-form :model="editForm">
<el-form-item label="账号">
<el-input v-model="editForm.account" disabled></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="editForm.password"></el-input>
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="editForm.name"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="updateAdmin">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import adminApi from '../api/admin'
export default {
setup() {
const admins = ref([])
const form = ref({
account: '',
password: '',
name: ''
})
const editForm = ref({
account: '',
password: '',
name: ''
})
const dialogVisible = ref(false)
const fetchAdmins = async () => {
try {
const response = await adminApi.getAllAdmins()
admins.value = response.data
} catch (error) {
ElMessage.error('获取管理员列表失败')
}
}
const addAdmin = async () => {
try {
await adminApi.addAdmin(form.value)
ElMessage.success('添加成功')
form.value = { account: '', password: '', name: '' }
fetchAdmins()
} catch (error) {
ElMessage.error('添加失败')
}
}
const handleEdit = (row) => {
editForm.value = { ...row }
dialogVisible.value = true
}
const updateAdmin = async () => {
try {
await adminApi.updateAdmin(editForm.value.account, editForm.value)
ElMessage.success('更新成功')
dialogVisible.value = false
fetchAdmins()
} catch (error) {
ElMessage.error('更新失败')
}
}
const handleDelete = async (account) => {
try {
await adminApi.deleteAdmin(account)
ElMessage.success('删除成功')
fetchAdmins()
} catch (error) {
ElMessage.error('删除失败')
}
}
onMounted(() => {
fetchAdmins()
})
return {
admins,
form,
editForm,
dialogVisible,
addAdmin,
handleEdit,
updateAdmin,
handleDelete
}
}
}
</script>
<style scoped>
.admin-management {
padding: 20px;
}
</style>
解析:
1.这里的表格用的是elemt组件库里面的表格标签。这里的导入:
import { ref, onMounted } from 'vue' import { ElMessage } from 'element-plus' import adminApi from '../api/admin'
可以删去,因为我设置了自动导入。
2.当点击编辑按钮的时候,会触发handleEdit方法,弹出编辑对话框。当点击删除按钮的时候,会触发handleDelete方法,删除一行数据。当点击添加按钮时,就会触发addAdmin方法。
当使用
<el-table>
的scoped-slot
(作用域插槽)时,Element UI 会自动向插槽传递一个scope
对象<template #default="scope"> <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button> <el-button size="small" type="danger" @click="handleDelete(scope.row.account)">删除</el-button> </template>
#default
:Vue 3 的简写语法,等同于v-slot:default
,表示默认插槽。
="scope"
:将插槽传递的属性对象命名为scope
(可自定义为其他名称,如slotProps
)。这样就可以用scope.row来访问当前行数据了,scope是响应式数据,修改scope.row会直接修改表格数据源。
3.editForm
是一个 响应式对象,用于临时存储和编辑表格中某一行的数据。它的本质是一个普通的 JavaScript 对象(Plain Object),但通过 Vue 的
ref
或reactive
包装后成为响应式对象。admins,form,dialogvisible同理。
4.dialogVisible
控制对话框显示/隐藏的逻辑是 Element Plus 组件库(基于 Vue 3)的内置功能。
控制显示:当
dialogVisible
为true
时,<el-dialog>
组件会显示;为false
时隐藏。双向绑定:通过
v-model
与<el-dialog>
绑定,无论是代码修改dialogVisible
还是用户点击对话框的关闭按钮,都会自动同步状态。这里采用的是Composition API 的标准用法,如果使用setup语法糖,就可以不需要手动return了,这里没用语法糖,return的作用是将数据暴露给模块,让模块可以访问。
5.:data="admins"
绑定的数据来源于 Vue 组件的响应式状态,具体是通过setup()
函数中定义的admins
变量。在
<script>
部分的setup()
函数中,admins
通过ref
创建并初始化:import { ref } from 'vue'; const admins = ref([]); // 初始化为空数组
ref([])
:创建一个响应式引用,初始值为空数组。
value
:在 JavaScript 中访问时需要admins.value
,但在模板中直接写admins
即可(Vue 自动解包)。在
fetchAdmins
方法中,通过 API 请求获取数据并赋值给admins
:const fetchAdmins = async () => {
try {
const response = await adminApi.getAllAdmins(); // 调用API
admins.value = response.data; // 将返回数据赋值给 admins
} catch (error) {
ElMessage.error('获取管理员列表失败');
}
};
adminApi.getAllAdmins()
:访问后端接口(GET /api/admins
),返回管理员列表。
response.data
:接口返回的 JSON 数据格式为:[
{ "account": "admin1", "name": "张三" },
{ "account": "admin2", "name": "李四" }
]
3.index.js
import { createRouter, createWebHistory } from 'vue-router'
import AdminManagement from '../components/AdminManagement.vue'
const routes = [
{
path: '/',
name: 'AdminManagement',
component: AdminManagement
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
解析:
配置路由。Vue Router 支持 三种主要的路由历史管理方式,这里采用的是html5模式。
对比总结
特性 createWebHistory()
createWebHashHistory()
createMemoryHistory()
URL 美观度 高 ( /path
)低 ( /#/path
)无 URL 变化 服务器配置 必需 无需 无需 SEO 支持 友好 不友好 不适用 浏览器兼容性 IE10+ 全兼容 全兼容 典型使用场景 生产环境 (SPA/SSR) 静态托管/旧浏览器 SSR/测试 如何选择?
优先选
createWebHistory()
如果控制服务器且需要专业部署(配置 Fallback 到index.html
)。备选
createWebHashHistory()
如果是静态网站托管(如 GitHub Pages)或需要兼容 IE9。特殊场景用
createMemoryHistory()
仅用于测试或 SSR 框架内部。
path: '/'
表示当用户访问网站的根路径时,会匹配这个路由,表示默认访问的根路径路由。
4.App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
margin: 20px;
}
</style>
解析:
<router-view>
会根据当前浏览器地址栏的 URL 路径,动态渲染匹配的路由组件。