springboot + vue 前后端结合·数据库查询
springboot + vue 前后端结合·数据库查询
数据库部分:
/* Navicat Premium Data Transfer Source Server : localHost Source Server Type : MySQL Source Server Version : 50529 Source Host : localhost:3306 Source Schema : vue-demo Target Server Type : MySQL Target Server Version : 50529 File Encoding : 65001 Date: 08/07/2020 17:57:30 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(10) NOT NULL AUTO_INCREMENT, `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `age` int(4) NOT NULL, `address` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, '张飞', 18, '三国'); INSERT INTO `user` VALUES (2, '刘备', 10, '三国'); INSERT INTO `user` VALUES (3, '关羽', 27, '三国'); INSERT INTO `user` VALUES (4, '曹操', 11, '三国'); INSERT INTO `user` VALUES (5, '吕布', 26, '中国 上海 浦东'); INSERT INTO `user` VALUES (6, '杜坡', 23, '北京'); SET FOREIGN_KEY_CHECKS = 1;
后端项目结构:
CORSConfig.java
@Configuration public class CORSConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurerAdapter() { @Override public void addCorsMappings(CorsRegistry registry) { // 限制了路径和域名的访问 registry.addMapping("/api*").allowedOrigins("http://localhost:8080"); } }; } }
UserController.java
package com.csyd.controller; import com.csyd.pojo.User; import com.csyd.pojo.vo.LoginVo; import com.csyd.pojo.vo.PageVo; import com.csyd.result.Result; import com.csyd.result.ResultGenerator; import com.csyd.service.UserService; import com.github.pagehelper.PageInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.Objects; @Controller @RequestMapping("/api") public class UserController { @Autowired UserService userService; @CrossOrigin //跨域请求必加 @RequestMapping("/user/login") @ResponseBody public Result login(@Valid @RequestBody LoginVo loginVo, BindingResult bindingResult){ if (bindingResult.hasErrors()) { String message = String.format("登陆失败,详细信息[%s]。", bindingResult.getFieldError().getDefaultMessage()); return ResultGenerator.genFailResult(message); } if (!Objects.equals("admin", loginVo.getUsername()) || !Objects.equals("123456", loginVo.getPassword())) { String message = String.format("登陆失败,详细信息[用户名、密码信息不正确]。"); return ResultGenerator.genFailResult(message); } return ResultGenerator.genSuccessResult(); } @CrossOrigin @RequestMapping("/user/userList") @ResponseBody public Result userList(@RequestBody PageVo pageVo){ PageInfo<User> userPage = userService.userPage(pageVo.getPageNum(), pageVo.getPageSize()); return ResultGenerator.genSuccessResult(userPage); } }
mapper.java
package com.csyd.dao; import com.csyd.pojo.User; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper public interface UserMapper { int deleteByPrimaryKey(Integer id); int insert(User record); int insertSelective(User record); User selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(User record); int updateByPrimaryKey(User record); List<User> selectAll(); }
LoginVo.java
package com.csyd.pojo.vo; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotNull; public class LoginVo { @NotNull(message="用户名不允许为空") @Length(min=3,max=16,message="用户名长度不正确!") private String username; @NotNull(message="密码不允许为空") @Length(min=6,max=16,message="密码长度不正确!") private String password; public String getUsername() { return username; } public String getPassword() { return password; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } }
PageVo.java
package com.csyd.pojo.vo; public class PageVo { private int pageNum; private int pageSize; public int getPageNum() { return pageNum; } public void setPageNum(int pageNum) { this.pageNum = pageNum; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } }
User.java
package com.csyd.pojo; import java.io.Serializable; public class User implements Serializable { private Integer id; private String name; private Integer age; private String address; private static final long serialVersionUID = 1L; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
Result.java
package com.csyd.result; /** * 统一API响应结果封装 */ public class Result { private int code; private String message; private Object data; public Result setCode(ResultCode resultCode) { this.code = resultCode.getCode(); return this; } public int getCode() { return code; } public Result setCode(int code) { this.code = code; return this; } public String getMessage() { return message; } public Result setMessage(String message) { this.message = message; return this; } public Object getData() { return data; } public Result setData(Object data) { this.data = data; return this; } }
ResultCode.java
package com.csyd.result; /** * 响应码枚举,参考HTTP状态码的语义 */ public enum ResultCode { SUCCESS(200, "sucess"),//成功 FAIL(400, "失败"),//失败 ; private Integer code; private String msg; ResultCode(int code) { this.code = code; } public Integer getCode() { return code; } public String getMsg() { return msg; } ResultCode(int code, String msg) { this.code = code; this.msg = msg; } }
ResultGenerator.java
package com.csyd.result; /** * 响应结果生成工具 */ public class ResultGenerator { private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS"; public static Result genSuccessResult() { return new Result() .setCode(ResultCode.SUCCESS) .setMessage(DEFAULT_SUCCESS_MESSAGE); } public static Result genSuccessResult(Object data) { return new Result() .setCode(ResultCode.SUCCESS) .setMessage(DEFAULT_SUCCESS_MESSAGE) .setData(data); } public static Result genFailResult(String message) { return new Result() .setCode(ResultCode.FAIL) .setMessage(message); } public static Result genFailResult(ResultCode resultCode) { return new Result() .setCode(resultCode.getCode()) .setMessage(resultCode.getMsg()); } }
UserService.java
package com.csyd.service; import com.csyd.dao.UserMapper; import com.csyd.pojo.User; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserService { @Autowired private UserMapper userMapper; public PageInfo<User> userPage(Integer pageNum, Integer pageSize){ PageHelper.startPage(pageNum, pageSize); List<User> userList = userMapper.selectAll(); PageInfo<User> userPageInfo = new PageInfo<>(userList); return userPageInfo; } }
package com.csyd; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Vueproject { public static void main(String[] args) { SpringApplication.run(Vueproject.class, args); } }
application.properties
server.port=8765 #数据库 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/vue-demo spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver #mybatis(注意必不可缺!不然找不到mapper.) mybatis.mapper-locations=classpath:/mapper/*Mapper.xml mybatis.type-aliases-package=com.csyd.pojo
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!-- defaultModelType="flat" 设置复合主键时不单独为主键创建实体 --> <context id="MySql" defaultModelType="flat"> <!-- 生成的POJO实现java.io.Serializable接口 --> <plugin type="org.mybatis.generator.plugins.SerializablePlugin" /> <!--注释--> <commentGenerator> <!-- 将数据库中表的字段描述信息添加到注释 --> <property name="addRemarkComments" value="true"/> <!-- 注释里不添加日期 --> <property name="suppressDate" value="true"/> </commentGenerator> <!-- 数据库连接 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/vue-demo" userId="root" password="root"/> <!-- 生成POJO对象,并将类放到com.songguoliang.springboot.entity包下 --> <javaModelGenerator targetPackage="com.csyd.pojo" targetProject="src/main/java"></javaModelGenerator> <!-- 生成mapper xml文件,并放到resources下的mapper文件夹下 --> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"></sqlMapGenerator> <!-- 生成mapper xml对应dao接口,放到com.songguoliang.springboot.mapper包下--> <javaClientGenerator targetPackage="com.csyd.dao" targetProject="src/main/java" type="XMLMAPPER"></javaClientGenerator> <!-- table标签可以有多个,至少一个,tableName指定表名,可以使用_和%通配符 --> <table schema="vue-demo" tableName="user" domainObjectName="User" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="false" enableUpdateByExample="false"> </table> </context> </generatorConfiguration>
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.csyd</groupId> <artifactId>vueproject</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <build> <plugins> <!--mybatis自动生成代码插件--> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.6</version> <configuration> <!-- 是否覆盖,true表示会替换生成的JAVA文件,false则不覆盖 --> <overwrite>true</overwrite> </configuration> <dependencies> <!--mysql驱动包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency> </dependencies> </plugin> </plugins> </build> <dependencies> <!--集成springmvc框架并实现自动配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--数据库--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--pagehelper分页--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.3</version> </dependency> </dependencies> </project>
PostMan测试一下接口:
搭建前端VUE
目录结构:
Layout.vue
<style scoped> .layout{ border: 1px solid #d7dde4; background: #f5f7f9; position: relative; border-radius: 4px; overflow: hidden; } .layout-header-bar{ background: #f4b1df; box-shadow: 0 1px 1px rgba(0,0,0,.1); } .layout-logo-left{ width: 90%; height: 30px; background: #5b6270; border-radius: 3px; margin: 15px auto; } .menu-icon{ transition: all .3s; } .rotate-icon{ transform: rotate(-90deg); } .menu-item span{ display: inline-block; overflow: hidden; width: 69px; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; transition: width .2s ease .2s; } .menu-item i{ transform: translateX(0px); transition: font-size .2s ease, transform .2s ease; vertical-align: middle; font-size: 16px; } .menu-item .ivu-menu-item{ color: darkgray; } .collapsed-menu span{ width: 0px; transition: width .2s ease; } .collapsed-menu i{ transform: translateX(5px); transition: font-size .2s ease .2s, transform .2s ease .2s; vertical-align: middle; font-size: 22px; } .layout-logo{ width: 200px; height: 50px; /*background: white;*/ border-radius: 3px; float: right; position: relative; top: 5px; right: 20px; text-align: center; background: url("../../assets/logo.png"); background-size: 200px 50px; } </style> <template> <div class="layout"> <Layout> <Header :style="{padding: 0}" class="layout-header-bar"> <Icon @click.native="collapsedSider" color="white" :class="rotateIcon" :style="{margin: '0 20px'}" type="md-menu" size="24"></Icon> <a style="color: white">退出登录</a> <div class="layout-logo" ></div> </Header> <Layout :style="{minHeight: '100vh'}"> <Sider :style="{background: '#fff'}" breakpoint="md" ref="side1" hide-trigger collapsible :collapsed-width="78" v-model="isCollapsed" theme="light" > <Menu active-name="1-1" theme="light" width="auto" :class="menuitemClasses"> <router-link to="/manage/page1"> <MenuItem name="1-1"> <Icon type="ios-navigate"></Icon> <span>Option 1</span> </MenuItem> </router-link> <router-link to="/manage/page2"> <MenuItem name="1-2"> <Icon type="ios-search"></Icon> <span>Option 2</span> </MenuItem> </router-link> <router-link to="/manage/page3"> <MenuItem name="1-3"> <Icon type="ios-settings"></Icon> <span>Option 3</span> </MenuItem> </router-link> </Menu> </Sider> <Layout :style="{background: '#fff'}"> <!--<Header :style="{padding: 0}" class="layout-header-bar">--> <!--<Icon @click.native="collapsedSider" :class="rotateIcon" :style="{margin: '0 20px'}" type="md-menu" size="24"></Icon>--> <!--</Header>--> <Content :style="{margin: '20px', background: '#fff', minHeight: '260px'}"> <router-view></router-view> </Content> </Layout> </Layout> </Layout> </div> </template> <script> export default { data () { return { isCollapsed: false } }, computed: { rotateIcon () { return [ 'menu-icon', this.isCollapsed ? 'rotate-icon' : '' ]; }, menuitemClasses () { return [ 'menu-item', this.isCollapsed ? 'collapsed-menu' : '' ] } }, methods: { collapsedSider () { this.$refs.side1.toggleCollapse(); } } } </script>
HelloWorld.vue
<template> <div class="hello"> <h1>{{ msg }}</h1> <h2>Essential Links</h2> <ul> <li> <a href="https://vuejs.org" target="_blank" > Core Docs </a> </li> <li> <a href="https://forum.vuejs.org" target="_blank" > Forum </a> </li> <li> <a href="https://chat.vuejs.org" target="_blank" > Community Chat </a> </li> <li> <a href="https://twitter.com/vuejs" target="_blank" > Twitter </a> </li> <br> <li> <a href="http://vuejs-templates.github.io/webpack/" target="_blank" > Docs for This Template </a> </li> </ul> <h2>Ecosystem</h2> <ul> <li> <a href="http://router.vuejs.org/" target="_blank" > vue-router </a> </li> <li> <a href="http://vuex.vuejs.org/" target="_blank" > vuex </a> </li> <li> <a href="http://vue-loader.vuejs.org/" target="_blank" > vue-loader </a> </li> <li> <a href="https://github.com/vuejs/awesome-vue" target="_blank" > awesome-vue </a> </li> </ul> </div> </template> <script> export default { name: 'HelloWorld', data () { return { msg: 'Welcome to Your Vue.js App' } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h1, h2 { font-weight: normal; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
Login.vue
<style> html,body { width: 100%; height: 100%; background: url("../assets/background.jpg"); } .from-wrap{ width: 100%; height: 260px; border-radius: 10px; background-color: #fff; padding: 20px 30px; margin-top: 60%; } h1 { text-align: center; margin-bottom: 20px; } FormItem { margin-bottom: 30px; } .form-footer { text-align: right; } </style> <template> <Row> <Col :xs="{ span: 20, offset: 2 }" :md="{ span: 6, offset: 16 }"> <div class="from-wrap"> <h1>登录</h1> <Form ref="loginVo" :model="loginVo" :rules="ruleValidate" > <FormItem prop="username"> <Input type="text" v-model="loginVo.username" placeholder="请输入用户名"> <Icon type="ios-person-outline" slot="prepend"></Icon> </Input> </FormItem> <FormItem prop="password"> <Input type="password" v-model="loginVo.password" placeholder="请输入密码"> <Icon type="ios-lock-outline" slot="prepend"></Icon> </Input> </FormItem> <FormItem class="form-footer"> <Button type="primary" @click="handleSubmit()">Submit</Button> </FormItem> </Form> </div> </Col> </Row> </template> <script> export default { data () { return { loginVo: { username:'', password:'' }, ruleValidate: { username: [ { required: true, message: '账号不能为空', trigger: 'blur' }, { min: 3, max: 16, message: '账号长度3-16个字符', trigger: 'blur' } ], password: [ { required: true, message: '密码不能为空', trigger: 'blur' }, { type: 'string', min: 6, max: 16, message: '密码长度6-16个字符', trigger: 'blur' } ] }, responseResult: [] } }, methods: { handleSubmit () { this.$axios .post('/user/login', { username: this.loginVo.username, password: this.loginVo.password }) .then(successResponse => { this.responseResult = JSON.stringify(successResponse.data); if (successResponse.data.code === 200) { this.$router.replace({path: '/manage/page1'}) }else { alert(successResponse.data.message) } }) .catch(function (error) { console.log(error); }); } } } </script>
Page1.vue
<template> <div> <Breadcrumb> <BreadcrumbItem >用户管理</BreadcrumbItem> </Breadcrumb> <Divider /> <Row> <Button icon="md-add" type="primary">添加</Button> </Row> <Table size="default" :columns="columns7" :data="data6" style="margin-top: 20px"></Table> <Page style="margin-top: 20px" :total="total" :page-size="pageSize" :page-size-opts="pageSizeOpts" show-total show-sizer @on-change="changePage" @on-page-size-change="changeSize"/> </div> </template> <script> export default { data () { return { columns7: [ //这里是table的头设置 { title: '名字', key: 'name', render: (h, params) => { return h('div', [ h('Icon', { props: { type: 'person' } }), h('strong', params.row.name) ]); } }, { title: '年龄', key: 'age' }, { title: '地址', key: 'address' }, { title: '操作', key: 'action', align: 'center', render: (h, params) => { return h('div', [ h('Button', { props: { type: 'default', size: 'small', }, style: { marginRight: '5px', }, on: { click: () => { this.show(params.index) } } }, '查看'), h('Button', { props: { type: 'primary', size: 'small' }, style: { marginRight: '5px' }, on: { // click: () => { // this.show(params.index) // } } }, '编辑'), h('Button', { props: { type: 'error', size: 'small' }, style: { marginRight: '5px' }, on: { // click: () => { // this.remove(params.index) // } } }, '删除') ]); } } ], data6: [], //用于接收table的数据 pageSize : 5, pageNum : 1, total : undefined, pageSizeOpts : [5, 10] } }, methods: { getUserList(){ //请求出去的是json this.$axios .post('/user/userList', { pageNum : this.pageNum, pageSize : this.pageSize }) .then(successResponse => { this.responseResult = JSON.stringify(successResponse.data); if (successResponse.data.code === 200) { this.data6 = successResponse.data.data.list; this.total = successResponse.data.data.total; }else { alert('系统错误'); } }) .catch(function (error) { console.log(error); }); }, changePage(page){ this.pageNum = page; this.getUserList(); }, changeSize(pageSize){ this.pageSize = pageSize; this.getUserList(); } }, created: function () { this.getUserList(); } } </script>
index.js
import Vue from 'vue' import Router from 'vue-router' import HelloWorld from '@/components/HelloWorld' import Login from '@/components/Login' import Layout from '@/components/common/Layout' import Page1 from '@/components/Page1' import Page2 from '@/components/Page2' import Page3 from '@/components/Page3' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'login', component: Login }, { path: '/hello', component: HelloWorld }, { path: '/manage', component: Layout, children: [ { path: '/manage/page1', name: 'page1', component: Page1 }, { path: '/manage/page2', name: 'page2', component: Page2 }, { path: '/manage/page3', name: 'page3', component: Page3 } ] } ] })
main.js
// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import router from './router' import iView from 'iview' //引入iview import 'iview/dist/styles/iview.css' //使用iview css import './theme/index.less'; import './css/common.css' // 引用axios,并设置基础URL为后端服务api地址 var axios = require('axios') //设置默认请求路径 axios.defaults.baseURL = 'http://localhost:8765/api' // 将API方法绑定到全局 Vue.prototype.$axios = axios Vue.use(iView, { transfer: true, size: 'large' }); Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
Vue启动
执行: npm install --save axios
访问:http://localhost:8080
userName:admin
passWord:123456