SpringBoot + Vue + Element 前后端分离的管理后台项目简单入门
这篇文章讲讲如何写一个简单的前后端分离的项目。技术栈用到了 Springboot 、 Vue 、element-UI 整合了 Mybatis 做一个管理后台数据的增删改查。
一.准备工作
① 环境搭建
- 安装 node.js,可以默认安装以及自定义安装(自行选择),配置系统环境变量。由于新版的 node.js 已经集成了 npm,所以 npm 也一并安装好了。
安装完毕后,打开 cmd.exe 分别输入下面的命令查看是否安装成功,若出现版本号则安装成功!
node -v npm -v
- 安装 vue 脚手架工具
首先安装npm的淘宝镜像(可选,下载速度比较快,推荐下载)
输入以下的命令
npm i -g cnpm --registry=https://registry.npm.taobao.org
输入cnpm -v ,不报错即安装成功!
注意:出现cnpm 是不是内部或者外部命令,不要着急,配置电脑环境变量将 node.js 安装路径中 x:../xxx/node_global 文件夹路径复制到 path 中即可
接着安装 vue 脚手架工具:
cnpm i -g vue-cli
cmd.exe 测试是否安装成功:
vue -V
- 安装 webpack 前端资源加载/打包工具。
在 cmd.exe 命令行执行:
cnpm install webpack -g
② 项目创建
这里我创建了一个空的 maven 项目,然后把 pom.xml 文件和 src 目录删除:
然后点击左上角的 file => New => module 创建一个 springboot 后端模块,项目结构如下所示:
接下来创建 vue 前端模块
点击 idea 下方 Termainal 打开终端控制台(等效于 cmd.exe),输入 vue init webpack 你的项目名
然后一路回车确认,这里主要是配置 vue 项目的一些配置,大家可以根据需要自行选择,具体内容介绍我放在下面:
-
Project name (baoge): -----项目名称,直接回车,按照括号中默认名字(注意这里的名字不能有大写字母,如果有会报错Sorry, name can no longer contain capital letters)
-
Project description (A Vue.js project): ----项目描述,也可直接点击回车,使用默认名字
-
Author (): ----作者,输入你的大名
-
untime + Compiler: recommended for most users 运行加编译,既然已经说了推荐,就选它了
-
Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specificHTML) are ONLY allowed in .vue files - render functions are required elsewhere 仅运行时,已经有推荐了就选择第一个了
-
Install vue-router? (Y/n) 是否安装vue-router,这是官方的路由,大多数情况下都使用,这里就输入 “y” 后回车即可。
-
Use ESLint to lint your code? (Y/n) 是否使用ESLint管理代码,ESLint是个代码风格管理工具,是用来统一代码风格的,这里我选 “n” 后回车。
-
接下来也是选择题Pick an ESLint preset (Use arrow keys) 选择一个ESLint预设,编写vue项目时的代码风格,这里我选 “n” 后回车。
-
Setup unit tests with Karma + Mocha? (Y/n) 是否安装单元测试,这里我选 “n” 后回车。
-
Setup e2e tests with Nightwatch(Y/n)? 是否安装e2e测试 ,这里我选 “n” 后回车。
接着只需等待即可,此时项目结构变成了这样:
生成好后,可以看到 Terminal 控制台输出:
我们需要根据提示,在命令行依次输入
cd vue-ui cnpm install npm run dev
此时可以看到提示 vue 程序启动成功,并且给出了前端页面访问路径
根据路径访问 http://localhost:8080
成功访问到了 vue 项目,此时前端项目就已经成功跑起来了!
二.功能实现
上面讲到了前端项目的搭建,这段主要写后端提供具体的接口以及前端项目如何调用后台接口
首先到 mysql 数据库新建一个 student 表
CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名字', `age` int(11) NULL DEFAULT NULL COMMENT '年龄', `gender` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '性别', `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '学生表' ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of student -- ---------------------------- INSERT INTO `student` VALUES (1, '张三', 15, '男', '111@qq.com'); INSERT INTO `student` VALUES (2, '李四', 16, '女', '222@qq.com'); INSERT INTO `student` VALUES (3, '王五', 14, '男', '333@qq.com'); INSERT INTO `student` VALUES (4, '赵六', 15, '男', '444@qq.com'); INSERT INTO `student` VALUES (5, '钱七', 13, '女', '555@qq.com'); SET FOREIGN_KEY_CHECKS = 1;
① 后端接口准备
到 pom.xml 添加需要的 jar 包
<!-- springboot web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
修改 springboot 配置,因为前面 vue项目使用了 8080 端口,所以我们把 SpringBoot项目的端口改成 9090,以及添加数据库配置,我的 Mysql 数据库是 5.7.17 版本,大家根据自己数据库版本导入 maven 依赖和配置
# 应用名称 spring.application.name=vue-module # 应用服务 WEB 访问端口 server.port=9090 # mysql数据库连接 spring.datasource.username=你的数据库账号 spring.datasource.password=你的数据库密码 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/数据库名?useUnicode=true&&useSSL=false&&characterEncoding=UTF-8&&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true # mybatis mybatis.mapper-locations=classpath: mybatis/*.xml mybatis xml文件存放地址 mybatis.type-aliases-package=com.lin.entity 你的实体地址
接着在 java 目录下创建四层架构包,resources 目录下创建 mybatis 文件夹用于存放 mapper.xml 文件
- controller 控制层:负责调用 service 层接口
- entity 实体层:实体类是属性对象,用于供给其他层引用,该层的属性往往和数据库的表结构各个字段相同
- mapper 数据层:mapper 层所定义的接口要实现对数据的操作
- service 业务逻辑层: 负责功能的编写,将所需要的功能都定义在一个 service 层的接口当中,
接下来的部分内容比较多,我就直接贴 crud 的代码不放截图了,大家也可以自己写
entity:
然后先从实体类开始,一一对应数据库字段,这里因为只是 demo 以及业务逻辑不复杂所以没有细分领域模型,如有需要可以自行分类
package com.lin.entity; public class Student { /** * id */ private Integer id; /** * 姓名 */ private String name; /** * 年龄 */ private Integer age; /** * 性别 */ private String gender; /** * 邮箱 */ private String email; //这里省略 get、set、toString 方法,实在太多了没意义,自行补上即可 }
mapper 接口:
package com.lin.mapper; import com.lin.entity.Student; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface StudentMapper { /** * 查询全部 Student 数据 * @return List<Student> */ List<Student> listStudent(); /** * 根据 id 查询一条 Student 数据 * @param id * @return Student */ Student selectStudentById(Integer id); /** * 新增一条 Student 数据 * @return */ int insertStudent(Student student); /** * 根据 id 删除一条 Student 数据 * @return */ int deleteStudentById(Integer id); /** * 更新一条 Student 数据 * @return */ int updateStudent(Student student); }
mapper.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lin.mapper.StudentMapper"> <resultMap type="com.lin.entity.Student" id="StudentResult"> <result property="id" column="id"/> <result property="name" column="name"/> <result property="age" column="age"/> <result property="gender" column="gender"/> <result property="email" column="email"/> </resultMap> <sql id="selectStudent"> select `id`,`name`,`age`,`gender`,`email` from student </sql> <select id="listStudent" resultMap="StudentResult"> <include refid="selectStudent"></include> </select> <select id="selectStudentById" resultMap="StudentResult"> <include refid="selectStudent"></include> where `id` = #{id}; </select> <insert id="insertStudent" parameterType="com.lin.entity.Student" useGeneratedKeys="true"> insert into student <trim prefix="(" suffix=")" suffixOverrides=","> <if test="name != null and name != ''">`name`,</if> <if test="age != null and age != ''">`age`,</if> <if test="gender != null and gender != ''">`gender`,</if> <if test="email != null and email != ''">`email`,</if> </trim> <trim prefix="values(" suffix=")" suffixOverrides=","> <if test="name != null and name != ''">#{name},</if> <if test="age != null and age != ''">#{age},</if> <if test="gender != null and gender != ''">#{gender},</if> <if test="email != null and email != ''">#{email},</if> </trim> </insert> <delete id="deleteStudentById" parameterType="integer"> delete from student where `id` = #{id} </delete> <update id="updateStudent" parameterType="com.lin.entity.Student"> update student <trim prefix="set" suffixOverrides=","> <if test="name != null and name != ''">`name` = #{name},</if> <if test="age != null and age != ''">`age` = #{age},</if> <if test="gender != null and gender != ''">`gender` = #{gender},</if> <if test="email != null and email != ''">`email` = #{email}</if> </trim> where `id` = #{id} </update> </mapper>
service 接口:
package com.lin.service; import com.lin.entity.Student; import java.util.List; public interface IStudentService{ /** * 查询全部 Student 数据 * @return List<Student> */ List<Student> listStudent(); /** * 根据 id 查询一条 Student 数据 * @param id * @return Student */ Student selectStudentById(Integer id); /** * 新增一条 Student 数据 * @return */ int insertStudent(Student student); /** * 根据 id 删除一条 Student 数据 * @return */ int deleteStudentById(Integer id); /** * 更新一条 Student 数据 * @return */ int updateStudent(Student student); }
serviceImpl:
package com.lin.service.impl; import com.lin.entity.Student; import com.lin.mapper.StudentMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.lin.service.IStudentService; import java.util.List; @Service public class StudentServiceImpl implements IStudentService{ @Autowired StudentMapper studentMapper; /** * 查询全部 Student 数据 * @return List<Student> */ @Override public List<Student> listStudent() { return studentMapper.listStudent(); } /** * 根据 id 查询一条 Student 数据 * @param id * @return Student */ @Override public Student selectStudentById(Integer id) { return studentMapper.selectStudentById(id); } /** * 新增一条 Student 数据 * @param student * @return */ @Override public int insertStudent(Student student) { return studentMapper.insertStudent(student); } /** * 根据 id 删除一条 Student 数据 * @param id * @return */ @Override public int deleteStudentById(Integer id) { return studentMapper.deleteStudentById(id); } /** * 更新一条 Student 数据 * @param student * @return */ @Override public int updateStudent(Student student) { return studentMapper.updateStudent(student); } }
controller:
package com.lin.controller; import com.lin.entity.Student; import com.lin.service.IStudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/student") public class StudentController { @Autowired IStudentService iStudentService; /** * 获取全部学生数据接口 * @return List<Student> */ @GetMapping(value = "/info",produces = {"application/json;charset=UTF-8"}) public List<Student> listStudentInfo(){ return iStudentService.listStudent(); } /** * 根据 id 查询一条 Student 数据接口 * @param id * @return Student */ @GetMapping(value = "/info/{id}",produces = {"application/json;charset=UTF-8"}) public Student studentInfoById(@PathVariable(value = "id")Integer id){ return iStudentService.selectStudentById(id); } /** * 新增一条 Student 数据 * @param student */ @PostMapping(value = "/add",produces = {"application/json;charset=UTF-8"}) public void addStudent(@RequestBody Student student){ iStudentService.insertStudent(student); } /** * 删除一条 Student 数据 * @param id */ @PostMapping(value = "/remove/{id}",produces = {"application/json;charset=UTF-8"}) public void removeStudent(@PathVariable("id") Integer id){ iStudentService.deleteStudentById(id); } /** * 修改一条 Student 数据 * @param student */ @PostMapping(value = "/edit",produces = {"application/json;charset=UTF-8"}) public void editStudent(@RequestBody Student student){ iStudentService.updateStudent(student); } }
写好后项目路径如下所示:
接口用 PostMan 测试正常,此时后端接口也准备完毕!
② 前端页面准备
接下来开始继续前端页面编写,估计看博客的你们也都是后端开发,所以前端页面估计也写得不太好看,好在有许多优秀的 UI 组件可以使用。这里我们使用 Element 组件库
组件库安装可以使用 npm 或 cdn安装(推荐 npm )
在 Termainal 终端控制台输入 cnpm i element-ui -S
后回车,稍等即可
修改 vue 项目根目录 main.js 添加下面三行即可完成 element 的安装
import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI);
接着开始新建一个新的 vue 页面
左上角 File => Settings 点击 Plugins 输入 Vue.js 并安装
在 vue 项目 src 目录新建一个 views 文件夹用于存放页面,右键点击 views 文件夹 new => Vue Conponent,然后在 template 标签里面写点东西
<div> <p>Hello,world!</p> <el-row> <el-button>默认按钮</el-button> <el-button type="primary">主要按钮</el-button> <el-button type="success">成功按钮</el-button> <el-button type="info">信息按钮</el-button> <el-button type="warning">警告按钮</el-button> <el-button type="danger">危险按钮</el-button> </el-row> </div>
接着修改路由,在 router 目录下 index.js 文件中添加 login 路径
import login from '@/views/login' Vue.use(Router) export default new Router({ routes: [ { path: '/login', name: 'login', component: login } ] })
然后到浏览器输入 http://localhost:8080/#/login
就可以看到效果了!
这个 vue 的 logo 我们不需要,所以到项目根目录把 App.vue 中 template 标签中的 <img src="./assets/logo.png">
给注释掉,刷新就会发现图标不见了,全局的 style 标签的内容不需要的话可以注释掉,然后添加以下代码减去 body 自身的边距
<style> body { padding: 0; margin: 0; } html,body,#app { height: 100%; } </style>
注意:正常前端项目修改后会自动重新编译,如果没有自动编译得到效果 按 ctrl + c
关闭项目后重新执行 npm run dev
重新编译项目即可
上面测试完页面路由以及 element 的引入后,我们需要把 login 页做成一个真正的登陆页面,所以修改代码如下:
login.vue:
<template> <div class="login-container"> <el-form :model="ruleForm2" :rules="rules2" status-icon ref="ruleForm2" label-position="left" label-width="0px" class="demo-ruleForm login-page"> <h3 class="title">系统登录</h3> <el-form-item prop="username"> <el-input type="text" v-model="ruleForm2.username" auto-complete="off" placeholder="用户名" ></el-input> </el-form-item> <el-form-item prop="password"> <el-input type="password" v-model="ruleForm2.password" auto-complete="off" placeholder="密码" ></el-input> </el-form-item> <el-checkbox v-model="checked" class="rememberme" >记住密码 </el-checkbox> <el-form-item style="width:100%;"> <el-button type="primary" style="width:100%;" @click="handleSubmit" :loading="logining">登录</el-button> </el-form-item> </el-form> </div> </template> <script> export default { name: "login", data(){ return { logining: false, ruleForm2: { username: 'admin', password: '123456', }, rules2: { username: [{required: true, message: 'please enter your account', trigger: 'blur'}], password: [{required: true, message: 'enter your password', trigger: 'blur'}] }, checked: false } }, methods: { handleSubmit(event){ this.$refs.ruleForm2.validate((valid) => { if(valid){ this.logining = true; if(this.ruleForm2.username === 'admin' && this.ruleForm2.password === '123456'){ this.logining = false; sessionStorage.setItem('user', this.ruleForm2.username); this.$router.push({path: '/index'}); }else{ this.logining = false; this.$alert('username or password wrong!', 'info', { confirmButtonText: 'ok' }) } }else{ console.log('error submit!'); return false; } }) } } } </script> <style scoped> .login-container { width: 100%; height: 100%; } .login-page { -webkit-border-radius: 5px; border-radius: 5px; width: 350px; padding: 35px 35px 15px; background: #fff; border: 1px solid #eaeaea; box-shadow: 0 0 25px #cac6c6; margin: 0; position: relative; top: 50%; left: 50%; transform: translate(-50%, -50%); } label.el-checkbox.rememberme { margin: 0px 0px 15px; text-align: left; } .title{ text-align: center; } </style>
效果如下:
登陆账号:admin 密码:123456
这个页面是我在网上简单找了个登陆页面 源码地址
然后接着创建一个登陆后跳转的页面 index.vue,我就不赘述了,跟上面创建 login 页面一样,然后引入 element 布局容器,就可以出现一个后台系统的布局框架了
index.vue 布局容器:
<template> <el-container> <el-aside width="200px">Aside</el-aside> <el-container> <el-header>Header</el-header> <el-main>Main</el-main> <el-footer>Footer</el-footer> </el-container> </el-container> </template> <script> export default { name: "index", } </script> <style scoped> .el-header, .el-footer { background-color: #B3C0D1; color: #333; text-align: center; line-height: 60px; height:100% } .el-aside { background-color: #D3DCE6; color: #333; text-align: center; line-height: 100vh; } .el-main { background-color: #E9EEF3; color: #333; text-align: center; line-height: 160px; } </style>
然后在 Aside 标签里面引入 element 侧边栏,修改样式,并且加上一点点细节
index.vue 侧边栏:
<template> <el-container> <!-- width的宽度跟collapse一样动态控制 --> <el-aside width="collapse"> <div class="logo" v-show="open"><h3><i class="el-icon-eleme"></i>xxx管理系统</h3></div> <div class="logo" v-show="close"><h3><i class="el-icon-eleme"></i></h3></div> <!-- :collapse="isCollapse" class="el-menu-vertical" 动态控制导航菜单的收起与展开 router:让index作为 path 进行路由跳转 --> <el-menu default-active="$route.path" router :default-openeds="openeds" :collapse="isCollapse" class="el-menu-vertical"> <el-submenu index="1"> <!-- 一级标题 --> <template slot="title"> <i class="el-icon-s-tools"></i> <span slot="title">后台管理</span> </template> <!-- 二级标题 --> <el-menu-item index="/console"> <i class="el-icon-setting"></i> <span slot="title">控制台</span> </el-menu-item> <el-menu-item index="/student"> <i class="el-icon-setting"></i> <span slot="title">学生管理</span> </el-menu-item> </el-submenu> </el-menu> </el-aside> <el-container> <el-header> <div class="trigger" @click="isShow"> <!-- 点击展开收起导航和切换对应图标 --> <i class="el-icon-s-fold" v-show="open"></i> <i class="el-icon-s-unfold" v-show="close"></i> </div> </el-header> <el-main> <router-view></router-view> </el-main> <el-footer>Footer</el-footer> </el-container> </el-container> </template> <script> export default { name: "index", data() { return { openeds: ["1"], isCollapse: false, //导航栏默认为展开 close: false, //第二个图标默认隐藏 open: true, //默认显示第一个图标 } }, methods: { isShow() { this.isCollapse = !this.isCollapse; this.open = !this.open; this.close = !this.close; }, } } </script> <style scoped> .el-header, .el-footer { background-color: #B3C0D1; color: #333; line-height: 60px; height: 100%; padding: 0 !important; } .el-aside { background-color: #D3DCE6; color: #333; height: 100vh; } .el-main { background-color: #E9EEF3; color: #333; } body > .el-container { margin-bottom: 40px; } .logo { height: 60px; line-height: 60px; background-color: antiquewhite; text-align: center; } .logo h3 { margin: 0; height: 60px; } .el-menu { border-right-width: 0; } .el-menu-vertical:not(.el-menu--collapse) { width: 240px; } .trigger { height: 60px; display: flex; align-items: center; justify-content: center; cursor: pointer; width: 54px; } .trigger i { font-size: 20px; } .trigger:hover { background-color: rgb(203, 215, 230); } </style>
效果如下:
展开:
收起:
中间 main 部分我加了个 <router-view></router-view>
目的是做一个路由嵌套,就是我们的控制台、学生管理页面将会被渲染在这里面
接下里创建 console 、student 页面:
然后到 router 文件夹下的 index.js 修改路由路径
import console from '@/views/console'; import student from '@/views/student'; { path: '/index', name: 'index', component: index, children:[ { path: '/console', name: 'console', component: console }, { path: '/student', name: 'student', component: student } ] }
将 index 页面路径重定向到 console 页面,children 声明子路由,这就完成了路由嵌套,到前端刷新页面,看到成功渲染 console 页面,
点击学生管理,也一样可以出现效果,这里就不展示了
接着我们修改 student 页面,引入 element 表格,对话框等控件,然后我们要考虑如何跟后台做交互,vue 推荐的是 axios 而不是 ajax。
首先要明白的是axios是什么:axios是基于promise(诺言)用于 node.js 和浏览器中。axios的作用是什么呢:axios主要是用于向后台发起请求的,还有在请求中做更多是可控功能。
我们在 Terminal 终端控制台输入 cnpm install axios --save-dev
然后到根目录下修改 main.js 完成挂载
import axios from 'axios' Vue.prototype.$axios = axios new Vue({ axios })
axios 的语法我也不详细说明了,基本上也都是填写那几个属性,想详细了解可以到 axios文档 看看
下面修改 student 页面,加入表格,弹出表单和一些按钮
<template> <el-card class="box-card"> <!-- Dialog 对话框 弹出新增和修改表单 --> <el-row> <el-button size="mini" type="primary" @click="add">新增</el-button> <el-dialog :title="title" :visible.sync="dialogFormVisible" width="30%"> <el-form :model="form" :rules="rules" ref="form"> <el-form-item label="id:" hidden> <el-input v-model="form.id"></el-input> </el-form-item> <el-form-item label="姓名:" prop="name"> <el-input v-model="form.name" placeholder="请输入姓名" style="width:80%"></el-input> </el-form-item> <el-form-item label="年龄:" prop="age"> <el-input v-model.number="form.age" placeholder="请输入年龄" style="width:80%"></el-input> </el-form-item> <el-form-item label="性别:" prop="gender"> <el-select v-model="form.gender" placeholder="请选择性别" style="width:80%"> <el-option label="男" value="男"></el-option> <el-option label="女" value="女"></el-option> </el-select> </el-form-item> <el-form-item label="邮箱:" prop="email"> <el-input v-model="form.email" placeholder="请输入邮箱" style="width:80%"></el-input> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="submit()">提 交</el-button> </div> </el-dialog> </el-row> <!-- 表格 --> <el-table ref="singleTable" :data="tableData" style="width: 100%"> <el-table-column type="selection" width="55"> </el-table-column> <el-table-column property="id" label="ID" width="50" align="center"> </el-table-column> <el-table-column property="name" label="姓名" width="120" align="center"> </el-table-column> <el-table-column property="age" label="年龄" width="120" align="center"> </el-table-column> <el-table-column property="gender" label="性别" width="120" align="center"> </el-table-column> <el-table-column property="email" label="邮箱" align="center"> </el-table-column> <el-table-column label="操作" align="center"> <template slot-scope="scope"> <el-button size="mini" @click="edit(scope.$index, scope.row)">编辑 </el-button> <el-button size="mini" type="danger" @click="remove(scope.$index, scope.row)">删除 </el-button> </template> </el-table-column> </el-table> </el-card> </template> <script> export default { name: "student", data() { return { title: '', currentRow: null, dialogFormVisible: false, form: {}, tableData: [], rules: { name: [{required: true, message: '请输入姓名', trigger: 'blur'}], age: [{required: true, message: '请输入年龄', trigger: 'blur'}, {type: 'number', message: '年龄必须为数字值', trigger: 'blur'}, {pattern: /^(0|[1-9]\d?|200)$/, message: '范围在0-200', trigger: 'blur'}], gender: [{required: true, message: '请选择性别', trigger: 'change'}], email: [{required: true, message: '请输入邮箱', trigger: 'blur'}] } } }, methods: { // 表单重置初始化 reset() { this.form = { id: null, name: null, age: null, gender: null, email: null } }, // 增 add() { this.reset() this.dialogFormVisible = true this.title = "新增学生数据" }, // 删 remove(index, row) { console.log(row.id) this.$axios({ method: 'post', url: 'http://localhost:9090/student/remove/' + row.id, }).then((response) => { this.$message({ message: '删除成功!', type: 'success' }); this.getList(); }).catch((error) => { }) }, // 改 edit(index, row) { this.reset() this.form = JSON.parse(JSON.stringify(row)); this.dialogFormVisible = true this.title = "修改学生数据" }, //查 getList() { this.$axios({ method: 'get', url: 'http://localhost:9090/student/info', }).then((response) => { this.tableData = response.data }).catch((error) => { }) }, //提交按钮 submit() { this.$refs['form'].validate((valid) => { if (valid) { if (this.form.id == null) { this.$axios({ method: 'post', data: this.form, url: 'http://localhost:9090/student/add', }).then((response) => { this.$message({ message: '新增成功!', type: 'success' }); this.dialogFormVisible = false this.getList(); }).catch((error) => { }) } else { this.$axios({ method: 'post', data: this.form, url: 'http://localhost:9090/student/edit', }).then((response) => { this.$message({ message: '修改成功!', type: 'success' }); this.getList(); this.dialogFormVisible = false }).catch((error) => { }) } } else { return false; } }) } }, mounted() { this.getList(); } } </script> <style scoped> </style>
注意: 因为我们前端项目是 8080 端口,后台项目是 9090端口,此时必然会遇到一个问题 " 跨域 "
什么是跨域
所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)端口号(port)
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。 同源策略是浏览器安全的基石
同源策略会阻止一个域的 javascript 脚本和另外一个域的内容进行交互。例如办公内外网环境,当我们访问外网一个恶意网站的时候,恶意网站就会利用我们的主机向内网的 url 发送 ajax 请求,破坏或盗取数据
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域:
当前页面url | 被请求页面url | 是否跨域 | 原因 |
---|---|---|---|
http://www.test.com/ |
http://www.test.com/index.html |
否 | 同源(协议、域名、端口号相同) |
http://www.test.com/ |
https://www.test.com/index.html |
跨域 | 协议不同(http/https) |
http://www.test.com/ |
http://www.baidu.com/ |
跨域 | 主域名不同(test/baidu) |
http://www.test.com/ |
http://blog.test.com/ |
跨域 | 子域名不同(www/blog) |
http://www.test.com:8080/ |
http://www.test.com:7001/ |
跨域 | 端口号不同(8080/7001) |
跨域可以在前端解决也可以在后端解决,解决方法也挺多种的,这里我偷懒就到 springboot 后台用注解解决
在控制层 StudentController 类上加一个注解 @CrossOrigin(origins = "*")
即可
点击新增或修改按钮弹出对话框表单
什么都先不填,直接点击提交,会发现表单输入框下有一行提示,因为我们上面的代码用了 element 表单验证,当然也可以自定义表单验证,这个可以非常友好的提示用户哪些选项为必填项,减少输入出错的情况。后台有需要也可以做一些判空操作,这样新增、编辑数据出错的情况就会减少
填入数据,点击提交
成功新增数据,编辑也同样道理,我就不演示了
点击删除,同样也可以删除成功!
总结
这样简单的前后端分离的增、删、改、查后台业务就完成了,但是还是有很多不完善的地方,很多地方都可以统一封装成组件等。
我看评论区有些小伙伴要源码的,我这边也找不到源代码了,你们先试试看照帖子的内容实现,有问题可以在评论区交流
本文作者:启航黑珍珠号
本文链接:https://www.cnblogs.com/Linzj5950/p/15817176.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步