第六章 MyBatis-Plus+Vue+Element前后端分离实战
分类: springboot 专栏: springboot3.0新教材 标签: mybatis-plus vue3
2024-01-03 22:58:21 1333浏览
前期回顾
- mybatisplus带条件查询 :
QueryWrapper 和LambdaQueryWrapper
- mybatisplus分页查询
实战项目展示
后端开发
依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
spring.datasource.url=jdbc:mysql:///bookstore?serverTimezone=Asia/Shanghai&characterEncoding=utf8
spring.datasource.password=123456
spring.datasource.username=root
@Data
@TableName(value = "student")
public class Student {
@TableId(value = "id",type = IdType.AUTO)
private int id;
private String studentname;
private String gender;
private int age;
private String address;
}
@Data
@AllArgsConstructor
public class ResultVo<T> {
private Integer code;
private String mess;
private T data;
//success
public static<T> ResultVo success(String mess,T data){
return new ResultVo(200,mess,data);
}
//error
public static<T> ResultVo error(String mess){
return new ResultVo(500,mess,null);
}
}
测试新增修改json
{
"id":20,
"studentname":"李四",
"gender":"女",
"age":30,
"address":"山西太原"
}
控制层
/**
* @author:xiaojie
* @create: 2024-01-04 10:33
* @Description:
*/
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
StudentService studentService;
//分页带条件查询
@GetMapping
public ResultVo page(String studentname, @RequestParam(defaultValue = "1") Integer pageNum ){
Page<Student> studentPage= studentService.getPage(studentname,pageNum);
return ResultVo.success("",studentPage);
}
//增 或者修改
@PostMapping
public ResultVo addOrUpdate(@RequestBody Student student){
studentService.saveOrUpdate(student);
return ResultVo.success("操作成功",null);
}
//删除
@DeleteMapping("/{id}")
public ResultVo del(@PathVariable Integer id){
studentService.removeById(id);
return ResultVo.success("操作成功",null);
}
}
前端开发
安装vue
先安装nvm ,然后通过nvm安装node和npm ,进而通过npm安装vue脚手架
这些之前都讲过了就不赘述了。
创建vue项目
前提:我用的node版本是14.17.5
- 1.vue create 项目名称
选择手动选功能
然后选择如下选项
注意:空格是选中
然后回车
是否保存为将来创建项目的预设,建议第一次创建的时候保存下
- 2.安装路由-这个前面创建项目的时候已经安装过了
npm install vue-router@next
- 3.安装axios
npm install axios -save
- 4.安装Element Plus
npm install element-plus --save
- 5.安装Element Plus中要用到的icon图标
npm install @element-plus/icons
- 6.运行项目
npm run serve
项目结构
- (1) node_models目录:
项目的依赖包,包含了很多基础依赖,如本项目依赖了vue-router、vuex都可以在该目录下面找得到,也可以根据开发的需要安装其他依赖,方法是在Visual Studio Code中点击菜单Terminal New Terminal打开终端(相当于 window系统的命令提示符),默认路径就是当前项目路径下,安装命令:npm install [依赖包的名称]
- (2) public目录:
公共资源目录,用于放置HTML文件和所需的图片(图标)。这里的index.html是程序的主页文件,一般只定义一个空的根节点<div id=“app”></div>用于挂载Vue实例。
- (3) src目录:
是项目的核心文件目录,其下包括以下子目录或文件
- assets:静态资源目录,存放css样式、图片、脚本和字体等等。
- components:组件目录,存放项目的各种组件。
- router:路由目录,在该目录下的index.js文件中配置路由。
- store:容器目录,用于状态管理。
- views:视图目录,用于存放路由配置中所用到的各种视图组件。
- App.vue:项目的主组件,是页面入口文件。
- main.js:入口javascript文件,由此文件创建Vue实例,串联起项目的各个部分。
- package.json文件:项目的描述及依赖
- package-lock.json:版本管理。
跨域问题解决方案
只需要在项目的vue.config.js文件中添加代码如下:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false
})
//以上是自动生成的,下面是为解决跨域加的
module.exports = {
publicPath:'/',
devServer: {
host:'localhost',
port:8088, //前端项目端口号 选填
proxy: { //设置代理,必须填
'/api': {
target: 'http://localhost:8080', // 配置访问的服务器地址
pathRewrite: { '^/api': '' }, // 用于将请求中的/api字符串替换为空, 然后访问地址就能正确访问,若不添加此行配置,那么访问地址就变成了: http://localhost:8080/api/request_url,这样的请求就会出现 404 操作
ws: true, // 是否支持 websocket, 默认是 true
changeOrigin: true // 用于控制请求头中的 host 值, 默认是 true
}
}
}
}
这里的意思是使用/api请求来代替真实的请求,浏览器代理会将/api请求重新替换成真实请求去访问,所以前端的所有请求前面要加上/api前缀(但前面不能有真实后台地址)。
前端核心代码
<template>
<div>
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item label="学生姓名">
<el-input v-model="formInline.studentname" placeholder="请输入学生姓名" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSearch">搜索</el-button>
<el-button type="warning" @click="onAdd">新增</el-button>
</el-form-item>
</el-form>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="id" label="编号" />
<el-table-column prop="studentname" label="学生姓名" />
<el-table-column prop="gender" label="性别" />
<el-table-column prop="age" label="年龄" />
<el-table-column prop="address" label="家庭住址" />
<el-table-column label="操作" >
<template #default="scope">
<el-button type="primary" size="small" @click="edit(scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="del(scope.row.id)">删除</el-button>
</template>
</el-table-column >
</el-table>
<!--分页-->
<el-pagination background
@current-change="changePage"
layout="total, prev, pager, next, jumper"
:total="total"
:page-count="pages" />
<!--弹框-->
<el-dialog
v-model="dialogVisible"
:destroy-on-close="true"
title="学生信息"
width="30%"
>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="120px"
class="demo-ruleForm"
status-icon
>
<el-form-item label="学生姓名" prop="studentname">
<el-input v-model="ruleForm.studentname" />
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="ruleForm.gender">
<el-radio label="男" />
<el-radio label="女" />
</el-radio-group>
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input v-model="ruleForm.age" />
</el-form-item>
<el-form-item label="家庭住址" prop="address">
<el-input v-model="ruleForm.address" type="textarea" />
</el-form-item>
<el-form-item>
<el-button @click="resetForm('ruleFormRef')">重置</el-button>
<el-button type="primary" @click="submitForm">
提交
</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script >
import {onMounted,toRefs,reactive,ref} from 'vue'
import axios from 'axios'
import { ElMessage,ElMessageBox } from "element-plus"
export default {
//setup () 是vue3新增加的组件。vue3采用了组合式 API ,
// 为了使用组合式API,我们需要一个入口,在vue3组件中,称之为setup。
// (简单点来说,就是vue2里面的data,method,computed,
// 所有数据方法全写在setup里)
setup(){
//reactive函数可以把复合类型的变量变为响应式的引用
const data = reactive({
formInline: {
studentname:''
},
ruleForm:{
studentname:'',
gender:'',
age:'',
address:''
},
rules:{
studentname: [
{ required: true, message: '请输入学生姓名', trigger: 'blur' },
],
gender: [
{ required: true, message: '请选择性别', trigger: 'blur' },
],
age: [
{ required: true, message: '请输入年龄', trigger: 'blur' },
{ pattern :/^(1[8-9]|[2-9]\d)$/,message: '必须是18-99之间的数字', trigger: 'blur' }
],
address: [
{ required: true, message: '请输入家庭地址', trigger: 'blur' },
],
},
dialogVisible:false,
tableData:[],
pageNum:1,
total:0,
pages:1,
})
const edit = (row) => {
data.ruleForm=row
data.dialogVisible=true
}
const onAdd = () => {
data.ruleForm={}
data.dialogVisible=true
}
const del = (id) => {
ElMessageBox.confirm(
'你确定要删除该记录吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
axios({
url:'/api/student/'+id,
method:'delete',
}).then(function (res){
if(res.data.code==200){
ElMessage({
type: 'success',
message: '已删除',
})
getTableData()
}else{
ElMessage({
type: 'error',
message: '删除失败',
})
}
})
})
.catch(() => {
ElMessage({
type: 'info',
message: '已取消',
})
})
}
const ruleFormRef = ref();//把基本类型变量变为响应式引用 获取数据值需要加.value
const submitForm = () => {
console.log(ruleFormRef.value)
ruleFormRef.value.validate(vaild => {
if (vaild) {
axios({
url: '/api/student',
method:"POST",
data:data.ruleForm
}).then(function (res) {
if(res.data.code ==200){
ElMessage.success("操作成功")
data.dialogVisible=false
getTableData()
}else{
ElMessage.error("系统异常")
}
})
} else {
console.log("数据校验失败")
ElMessage.error("请按提示填写表单")
}
})
}
const resetForm = () => {
ruleFormRef.value.resetFields();
}
const changePage = (pageNum) => {
data.pageNum=pageNum
getTableData()
}
const onSearch = () => {
data.pageNum=1
getTableData()
}
const getTableData=function () {
axios({
url:'/api/student?pageNum='+data.pageNum+'&stuName='+data.formInline.studentname,
method:'get',
}).then(function (res) {
console.log(res)
data.tableData=res.data.data.records
data.pages=res.data.data.pages
data.total=res.data.data.total
})
}
onMounted(() =>{
getTableData()
})
return{
...toRefs(data),//可以将reactive返回的对象中的属性都转成ref;
edit,
getTableData,
changePage,
onAdd,
del,
onSearch,
submitForm,
ruleFormRef,
resetForm
}
}
}
</script>
<style scoped>
</style>
还有另外一种写法,vue3不能直接使用this,可以换成下面这种写法
<template>
<div>
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item label="学生姓名">
<el-input v-model="formInline.studentname" placeholder="请输入学生姓名" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">查询</el-button>
<el-button type="danger" @click="onAdd">新增</el-button>
</el-form-item>
</el-form>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="id" label="编号" />
<el-table-column prop="studentname" label="姓名" />
<el-table-column prop="gender" label="性别" />
<el-table-column prop="age" label="年龄" />
<el-table-column prop="address" label="家庭住址" />
<el-table-column label="操作" >
<template #default="scope">
<el-button type="primary" size="small" @click="onEdit(scope.row)">编辑</el-button>
<el-button type="danger" size="small" @click="onDel(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination background
layout="total, prev, pager, next,jumper"
:total="total"
:page-count="pages"
:current-page="pageNum"
@current-change="changePageNum"
/>
<el-dialog
v-model="dialogVisible"
title="学生信息"
width="30%"
:destroy-on-close="true"
>
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="120px"
class="demo-ruleForm"
status-icon
>
<el-form-item label="学生姓名" prop="studentname">
<el-input v-model="ruleForm.studentname" />
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-radio-group v-model="ruleForm.gender">
<el-radio label="男" />
<el-radio label="女" />
</el-radio-group>
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input v-model="ruleForm.age" />
</el-form-item>
<el-form-item label="家庭住址" prop="address">
<el-input v-model="ruleForm.address" type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm">
提交
</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import {reactive,toRefs,onMounted,getCurrentInstance} from 'vue'
import axios from 'axios'
import { ElMessage,ElMessageBox } from 'element-plus'
export default {
name: "StuView",
setup(){
const {proxy}=getCurrentInstance()
const data = reactive({
dialogVisible:false,
formInline:{
studentname: ''
},
ruleForm:{
studentname: '',
gender:'',
age:20,
address: ''
},
rules:{
studentname: [
{ required: true, message: '请输入学生名称', trigger: 'blur' },
],
gender: [
{ required: true, message: '请选择性别', trigger: 'blur' },
],
address: [
{ required: true, message: '请输入家庭地址', trigger: 'blur' },
],
age: [
{ required: true, message: '请输入年龄', trigger: 'blur' },
{ pattern: /^(1[8-9]|[2-9]\d)$/,message: '请输入18-99的数字',trigger: 'blur' }
],
},
pageNum:1,
tableData:[],
total:0,
pages:0,
})
const resetForm = () => {
proxy.$refs['ruleFormRef'].resetFields();
}
const submitForm = () => {
proxy.$refs['ruleFormRef'].validate((valid) => {
if (valid) {
axios({
url:'/api/student',
method: 'post',
data:data.ruleForm
}).then(function (res){
if(res.data.code==200){
ElMessage.success("操作成功")
data.dialogVisible=false
getBackData()
}else{
ElMessage.error("出现异常")
}
})
} else {
ElMessage.error("请按提示填写表单")
return false;
}
});
}
const changePageNum = (pageNo) => {
data.pageNum=pageNo
getBackData()
}
const onDel = (id) => {
ElMessageBox.confirm(
'确定要删除该记录吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
axios({
url:'/api/student/'+id,
method:'delete'
}).then(function (res){
if(res.data.code==200){
ElMessage({
type: 'success',
message: '删除成功',
})
getBackData()
}else{
ElMessage({
type: 'error',
message: '删除失败',
})
}
})
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消操作',
})
})
}
const onEdit = (row) => {
data.dialogVisible=true
data.ruleForm=row
}
const onAdd = () => {
data.ruleForm={
studentname: '',
gender:'',
age:20,
address: ''
}
data.dialogVisible=true
}
const onSubmit = () => {
console.log('submit!')
data.pageNum=1
getBackData()
}
onMounted(function () {
getBackData()
})
const getBackData = () => {
axios({
url: '/api/student?stuName='+data.formInline.studentname+'&pageNum='+data.pageNum,
method:'get'
}).then(function (res){
console.log(res)
data.tableData=res.data.data.records
data.pageNum=res.data.data.current
data.total=res.data.data.total
data.pages=res.data.data.pages
})
}
return {
...toRefs(data),
onDel,
onEdit,
resetForm,
submitForm,
getBackData,
onSubmit,
onAdd,
changePageNum
}
}
}
</script>
<style scoped>
</style>
vue2和vue3的主要区别
Vue3组合式APl(Composition API)主要用于在大型组件中提高代码逻辑的可复用性。
传统的组件随着业务复杂度越来越高,代码量会不断的加大,整个代码逻辑都不易阅读和理解。Vue3使用组合式API的地方为setup 。
在setup中,我们可以按逻辑关注点对部分代码进行分组,然后提取逻辑片段并与其他组件共享代码。因此,组合式APl (Composition API)允许我们编写更有条理的代码。
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
展开评论
您可能感兴趣的博客