springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能

推荐文章:
springboot整合redis并在项目中使用
vue+springboot 登录注册功能


目录

  • 一、创建数据库
  • 二、后端环境搭建
  • 三、实现验证码功能
    • CreateImageCode .java
    • 新建UserController.java
    • 编写接口:/user/getImage
    • 效果:
  • 四、开发
    • 1、创建前端项目
    • 2、注册登录前端开发
    • 3、注册登录后端开发
    • 4、省份增删改查及分页后端开发
    • 5、省份增删改查及分页前端开发
    • 6、效果

一、创建数据库

数据库名:travels

用户表 SQL:

CREATE TABLE t_user(
	id INT(6) PRIMARY KEY AUTO_INCREMENT,
	username VARCHAR(60),
	password VARCHAR(60),
	email VARCHAR(60)
);

省份表 SQL:

CREATE TABLE t_province(
	id INT(6) PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(60),
	tags VARCHAR(80),
	placecounts INT(4)
);

景点表 SQL:

CREATE TABLE t_place(
	id INT(6) PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(60),
	picpath MEDIUMTEXT,
	hottime	TIMESTAMP,
	hotticket	DOUBLE(7,2),
	dimticket	DOUBLE(7,2),
	placedes	VARCHAR(300),
	provinceid	INT(6) REFERENCES t_province(id)
);

二、后端环境搭建

springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第1张图片springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第2张图片

  • 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.travel</groupId>
    <artifactId>travels</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>travels</name>
    <description>travels</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.19</version>
        </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>
            <scope>runtime</scope>
        </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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第3张图片

server.port=8081
spring.application.name=travels

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/travels?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.name=root
spring.datasource.password=123456

mybatis.mapper-locations=classpath:com/travel/mapper/*.xml
mybatis.type-aliases-package=com.travel.travels.entity

三、实现验证码功能

springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第4张图片

CreateImageCode .java

package com.travel.travels.utils;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


public class CreateImageCode {
    // 图片的宽度。
    private int width = 160;
    // 图片的高度。
    private int height = 40;
    // 验证码字符个数
    private int codeCount = 4;
    // 验证码干扰线数
    private int lineCount = 20;
    // 验证码
    private String code = null;
    // 验证码图片Buffer
    private BufferedImage buffImg = null;
    Random random = new Random();

    public CreateImageCode() {
        creatImage();
    }

    public CreateImageCode(int width, int height) {
        this.width = width;
        this.height = height;
        creatImage();
    }

    public CreateImageCode(int width, int height, int codeCount) {
        this.width = width;
        this.height = height;
        this.codeCount = codeCount;
        creatImage();
    }

    public CreateImageCode(int width, int height, int codeCount, int lineCount) {
        this.width = width;
        this.height = height;
        this.codeCount = codeCount;
        this.lineCount = lineCount;
        creatImage();
    }

    // 生成图片
    private void creatImage() {
        int fontWidth = width / codeCount;// 字体的宽度
        int fontHeight = height - 5;// 字体的高度
        int codeY = height - 8;

        // 图像buffer
        buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = buffImg.getGraphics();
        //Graphics2D g = buffImg.createGraphics();
        // 设置背景色
        g.setColor(getRandColor(200, 250));
        g.fillRect(0, 0, width, height);



        // 设置字体
        //Font font1 = getFont(fontHeight);
        Font font = new Font("Fixedsys", Font.BOLD, fontHeight);
        g.setFont(font);

        // 设置干扰线
        for (int i = 0; i < lineCount; i++) {
            int xs = random.nextInt(width);
            int ys = random.nextInt(height);
            int xe = xs + random.nextInt(width);
            int ye = ys + random.nextInt(height);
            g.setColor(getRandColor(1, 255));
            g.drawLine(xs, ys, xe, ye);
        }

        // 添加噪点
        float yawpRate = 0.01f;// 噪声率
        int area = (int) (yawpRate * width * height);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);

            buffImg.setRGB(x, y, random.nextInt(255));
        }


        String str1 = randomStr(codeCount);// 得到随机字符
        this.code = str1;
        for (int i = 0; i < codeCount; i++) {
            String strRand = str1.substring(i, i + 1);
            g.setColor(getRandColor(1, 255));
            // g.drawString(a,x,y);
            // a为要画出来的东西,x和y表示要画的东西最左侧字符的基线位于此图形上下文坐标系的 (x, y) 位置处

            g.drawString(strRand, i*fontWidth+3, codeY);
        }


    }

    // 得到随机字符
    private String randomStr(int n) {
        String str1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
        String str2 = "";
        int len = str1.length() - 1;
        double r;
        for (int i = 0; i < n; i++) {
            r = (Math.random()) * len;
            str2 = str2 + str1.charAt((int) r);
        }
        return str2;
    }

    // 得到随机颜色
    private Color getRandColor(int fc, int bc) {// 给定范围获得随机颜色
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    /**
     * 产生随机字体
     */
    private Font getFont(int size) {
        Random random = new Random();
        Font font[] = new Font[5];
        font[0] = new Font("Ravie", Font.PLAIN, size);
        font[1] = new Font("Antique Olive Compact", Font.PLAIN, size);
        font[2] = new Font("Fixedsys", Font.PLAIN, size);
        font[3] = new Font("Wide Latin", Font.PLAIN, size);
        font[4] = new Font("Gill Sans Ultra Bold", Font.PLAIN, size);
        return font[random.nextInt(5)];
    }

    // 扭曲方法
    private void shear(Graphics g, int w1, int h1, Color color) {
        shearX(g, w1, h1, color);
        shearY(g, w1, h1, color);
    }

    private void shearX(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(2);

        boolean borderGap = true;
        int frames = 1;
        int phase = random.nextInt(2);

        for (int i = 0; i < h1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(0, i, w1, 1, (int) d, 0);
            if (borderGap) {
                g.setColor(color);
                g.drawLine((int) d, i, 0, i);
                g.drawLine((int) d + w1, i, w1, i);
            }
        }

    }

    private void shearY(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(40) + 10; // 50;

        boolean borderGap = true;
        int frames = 20;
        int phase = 7;
        for (int i = 0; i < w1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(i, 0, 1, h1, 0, (int) d);
            if (borderGap) {
                g.setColor(color);
                g.drawLine(i, (int) d, i, 0);
                g.drawLine(i, (int) d + h1, i, h1);
            }

        }

    }

    public void write(OutputStream sos) throws IOException {
        ImageIO.write(buffImg, "png", sos);
        sos.close();
    }

    public BufferedImage getBuffImg() {
        return buffImg;
    }

    public String getCode() {
        return code.toLowerCase();
    }

    //使用方法
// public void getCode3(HttpServletRequest req, HttpServletResponse response, HttpSession session) throws IOException{
//        // 设置响应的类型格式为图片格式
//            response.setContentType("image/jpeg");
//            //禁止图像缓存。
//            response.setHeader("Pragma", "no-cache");
//            response.setHeader("Cache-Control", "no-cache");
//            response.setDateHeader("Expires", 0);
//            CreateImageCode vCode = new CreateImageCode(100,30,5,10);
//            session.setAttribute("code", vCode.getCode());
//            vCode.write(response.getOutputStream());
//     }

}

新建UserController.java

springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第5张图片

编写接口:/user/getImage

@RestController
@RequestMapping("/user")
@CrossOrigin // 允许跨域(前后端分离)
@Slf4j // 日志对象
public class UserController {
    /**
     * 生成验证码
     * @throws IOException
     */
    @GetMapping("/getImage")
    public Map<String, String> getImage(HttpServletRequest request) throws IOException {
        Map<String, String> result = new HashMap<>();
        CreateImageCode createImageCode = new CreateImageCode();
        // 获取验证码
        String securityCode = createImageCode.getCode();
        // 验证码存入session
        String key = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        request.getServletContext().setAttribute(key, securityCode);
        // 生成图片
        BufferedImage image = createImageCode.getBuffImg();
        //进行base64编码
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ImageIO.write(image, "png", bos);
        String string = Base64Utils.encodeToString(bos.toByteArray());
        result.put("key", key);
        result.put("image", string);
        return result;
    }
}

效果:

前端展示:
springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第6张图片
Base64:
springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第7张图片

四、开发

1、创建前端项目

参考这里的第一点:https://blog.csdn.net/m0_45234510/article/details/106414466

2、注册登录前端开发

springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第8张图片springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第9张图片

  • 先安装一下axios:npm install axios --save
  • 然后在main.js加入:
import axios from 'axios';

Vue.prototype.axios = axios;
axios.defaults.baseURL = "/api"
axios.defaults.withCredentials = true
Vue.use(axios);
  • 在config/index.js中的proxyTable加入下面的api解决跨域问题
proxyTable: {
      '/api': {
        target:'http://localhost:8081', // 你请求的第三方接口
        changeOrigin:true, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
        pathRewrite:{  // 路径重写,
          '^/api': ''  // 替换target中的请求地址,也就是说以后你在请求 http://47.115.12.243 这个地址的时候直接写成/api即可。
        }
      },
    },
  • 在router/index.js配置路由
    springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第10张图片
  • Register.vue
<template>
    <div>
        <div id="wrap">
            <div id="header">
                <div style="float: right;padding-top: 24px"><span v-text="time"/> &emsp; </div>
                <h1>旅游信息管理系统</h1>
            </div>
            <div id="header-bar"></div>
            <div id="content" style="height: 360px">
                <img src="img/timg.jpg" style="float: right;height: 320px">
                <h2>注册</h2>
                <form action="province/provincelist.html" method="post">
                    <label>
                        <div class="label-text">&emsp;号:</div>
                        <input type="text" v-model="user.username" name="username">
                    </label>
                    <label>
                        <div class="label-text">&emsp;码:</div>
                        <input type="password" v-model="user.password" name="password">
                    </label>
                    <label>
                        <div class="label-text">&emsp;箱:</div>
                        <input type="text" v-model="user.email" name="email">
                    </label>
                    <!--前后端分离的架构, 动态访问验证码-->
                    <img :src="src" id="img-vcode" @click="getImage" :key="key">
                    <label>
                        <div class="label-text">验证码:</div>
                        <input type="text" v-model="code" name="vcode" style="width: 100px">
                    </label>
                    <button type="button" @click="saveUserInfo()">提 交</button>&emsp;
                    <a href="login.html">去登录</a>
                </form>
            </div>
            <div id="footer">
                yusael~
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        name: "Register",
        data() {
            return {
                user: {},
                code: "",
                src: "",
                key: "",
                time: "",
            }
        },
        methods: {
            saveUserInfo() {
                if (!this.user.username) {
                    alert('用户名不能为空!');
                    return;
                }
                if (!this.user.password) {
                    alert('密码不能为空!');
                    return;
                }
                if (!this.user.email) {
                    alert('邮箱不能为空!');
                    return;
                }
                // 发送axios
                this.axios.post("/user/register?code=" + this.code + "&key=" + this.key, this.user).then((res) => {
                    console.log(res);
                    alert("22")
                    if (res.data.state) {
                        alert(res.data.msg + ",点击确定跳转到登录页面!!!");
                        // location.href = './login.html';
                    } else {
                        alert(res.data.msg);
                    }
                });
            },
            getImage() {
                const _this = this;
                this.axios.get("/user/getImage").then((res) => {
                    console.log(res);
                    _this.src = "data:image/png;base64," + res.data.image;
                    _this.key = res.data.key;
                })
            },
        },
        created() {
            this.getImage(); // 获取验证码
            let now = new Date();
            this.time = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
        }
    }
</script>

<style scoped>
 form {
            width: 270px;
        }

        input {
            width: 70%;
            background: #eee;
        }

        input:focus {
            background: #fff;
        }

        form {
            padding: 0 12px 12px;
        }

        label {
            display: block;
            padding-bottom: 12px;
        }

        #img-vcode {
            width: 56px;
            height: 21px;
            float: right;
            position: relative;
            top: 2px;
            left: -6px
        }

        .label-text {
            width: 30%;
            float: left;
        }
</style>

  • Login.vue
<template>
   <div>
        <div id="wrap">
            <div id="header">
                <div style="float: right;padding-top: 24px"><span v-text="time"/>&emsp;</div>
                <h1>旅游信息管理系统</h1>
            </div>
            <div id="header-bar"></div>
            <div id="content" style="height: 360px">
                <img src="img/timg.jpg" style="float: right;height: 320px">
                <h2>登录</h2>
                <form action="province/provincelist.html" method="post">
                    <label>
                        <div class="label-text">&emsp;号:</div>
                        <input type="text" v-model="user.username" name="username">
                    </label>
                    <label>
                        <div class="label-text">&emsp;码:</div>
                        <input type="password" v-model="user.password" name="password">
                    </label>
                    <!--前后端分离的架构, 动态访问验证码-->
                    <img :src="src" :key="key" @click="getImage" id="img-vcode" style="width: 80px;">
                    <label>
                        <div class="label-text">验证码:</div>
                        <input type="text" v-model="code" name="vcode" style="width: 100px">
                    </label>
                    <button type="button" @click="login">提 交</button>&emsp;
                    <!-- <a href="/register">去注册</a> -->
                    <button type="button" @click="toregister">去注册</button>
                </form>
            </div>
            <div id="footer">
                yusael~
            </div>
        </div>
   </div>
</template>

<script>
    export default {
        name: "Login",
        data() {
            return {
                user: {},
                code: "",
                time: "",
                src: "",
                key: "",
            }
        },
        methods: {
            login() { // 登录
                if (!this.user.username) {
                    alert('用户名不能为空!');
                    return;
                }
                if (!this.user.password) {
                    alert('密码不能为空!');
                    return;
                }
                this.axios.post('/user/login?code=' + this.code + "&key=" + this.key, this.user).then((res) => {
                    console.log(res.data);
                    localStorage.setItem("userid", res.data.userId);
                    if (res.data.state) {
                        alert(res.data.msg + "点击确定进入主页!");
                        location.href = './province/provincelist.html';
                    } else {
                        alert(res.data.msg);

                    }
                });
            },
            getImage() {
                const _this = this;
                this.axios.get("/user/getImage").then((res) => {
                    console.log(res.data);
                    _this.src = "data:image/png;base64," + res.data.image;
                    _this.key = res.data.key;
                })
            },
            toregister(){
                this.$router.push("/register")
            }
        },
        created() {
            this.getImage(); // 获取验证码
            let now = new Date();
            this.time = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
        },
    }
</script>

<style scoped>

</style>

3、注册登录后端开发

  • 目录结构
    springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第11张图片
  • User.java
package com.travel.travels.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
    private String id;
    private String username;
    private String password;
    private String email;
}

  • Result.java (将状态封装起来)
package com.travel.travels.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true) // 链式调用
public class Result {
    private Boolean state = true;
    private String msg;
    private String userId;
}

  • UserDao.java
package com.travel.travels.dao;

import com.travel.travels.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserDao {
    // 注册用户
    void save(User user);
    // 根据用户名查询用户
    User findByUsername(String username);
}

  • UserMapper.xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.travel.travels.dao.UserDao">

    <!--注册用户-->
    <insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id"> /*id自动生成, 生成之后放到id属性*/
        INSERT INTO t_user
        VALUES (#{id}, #{username}, #{password}, #{email})
    </insert>

    <!--根据用户名查询用户|用户登录-->
    <select id="findByUsername" parameterType="String" resultType="com.travel.travels.entity.User">
        SELECT * FROM t_user
        WHERE username = #{username}
    </select>

</mapper>
  • UserService.java
package com.travel.travels.service;

import com.travel.travels.entity.User;

public interface UserService {
    void register(User user);
    User login(User user);
}

  • UserServiceImpl.java
package com.travel.travels.service;

import com.travel.travels.dao.UserDao;
import com.travel.travels.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class UserServiceImpl implements UserService{

    @Autowired
    private UserDao userDao;

    @Override
    public void register(User user) {
        if (userDao.findByUsername(user.getUsername()) == null) {
            userDao.save(user);
        } else {
            throw new RuntimeException("用户名已存在!");
        }
    }

    @Override
    public User login(User user) {
        User userDB = userDao.findByUsername(user.getUsername());
        if (userDB != null) {
            if (userDB.getPassword().equals(user.getPassword())) {
                return userDB;
            }
            throw new RuntimeException("密码输入错误!");
        } else {
            throw new RuntimeException("用户名输入错误!");
        }
    }

}

4、省份增删改查及分页后端开发

目录结构:
springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第12张图片

  • Province.java
package com.travel.travels.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Accessors(chain = true)  //chain 若为true,则setter方法返回当前对象
public class Province {
    private String id;
    private String name;
    private String tags;
    private Integer placecounts;

}

  • BaseDao.java(抽取出通用的CRUD)
package com.travel.travels.dao;

import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface BaseDao<T,K> {
    void save(T t);
    void update(T t);
    void delete(K k);
    T findOne(K k);

    List<T> findAll();
    List<T> findByPage(@Param("start") Integer start, @Param("rows") Integer row);
    Integer findTotals();

}

  • ProvinceDao.java
package com.travel.travels.dao;

import com.travel.travels.entity.Province;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ProvinceDao extends BaseDao<Province, String>{

}

  • ProvinceMapper.xml
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.travel.travels.dao.ProvinceDao">

    <!--分页查询所有-->
    <select id="findByPage" resultType="Province">
        select id,name,tags,placecounts
        from t_province
        order by placecounts
        limit #{start}, #{rows}
    </select>

    <!--查询总条数,用于计算分页-->
    <select id="findTotals" resultType="Integer">
        select count(id) from t_province
    </select>

    <!--通过名称查询-->
    <select id="findByName" resultType="Province" parameterType="String">
        select *
        from t_province
        where name like "%" #{name} "%"
    </select>

    <!--省份添加-->
    <insert id="save" parameterType="Province" keyProperty="id" useGeneratedKeys="true">
         insert into t_province values (#{id},#{name},#{tags},#{placecounts})
    </insert>

    <!--删除省份-->
    <delete id="delete" parameterType="String">
        delete from t_province where id = #{id}
    </delete>

    <!--查询一个-->
    <select id="findOne" resultType="Province">
        select id,name,tags,placecounts
        from t_province
        where id = #{id}
    </select>

    <!--修改省份-->
    <update id="update" parameterType="Province">
        update t_province
        set name = #{name}, tags = #{tags}, placecounts = #{placecounts}
        where id = #{id}
    </update>

</mapper>
  • ProvinceService.java
package com.travel.travels.service;

import com.travel.travels.entity.Province;

import java.util.List;

public interface ProvinceService {
    //page:当前页,rows:每页显示记录数
    List<Province> findByPage(Integer page,Integer rows);
    //查询
    List<Province> findByName(String name);
    //查询总条数
    Integer findTotals();
    //保存省份
    void save(Province province);
    //删除省份
    void delete(String id);
    //查询一个省份
    Province findOne(String id);
    //修改省份
    void update(Province province);
}


  • ProvinceServiceImpl.java
package com.travel.travels.service;

import com.travel.travels.dao.ProvinceDao;
import com.travel.travels.entity.Province;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional //捕获异常时,要想使事务生效,需要手动抛出RuntimeException异常或者添加rollbackFor = Exception.class
public class ProvinceServiceImpl implements ProvinceService{

    @Autowired
    ProvinceDao provinceDao;

    //分页查找
    @Override
    public List<Province> findByPage(Integer page, Integer rows) {
        // 传入的是当前页数, 以及页面显示的数量
        // 所以要根据这两个参数计算从mysql中查询数据要从第几行开始查几条
        int state = (page - 1) * rows; // 计算要查询的数据是从第几条数据开始的
        return provinceDao.findByPage(state,rows);
    }

    //查询所有
    @Override
    public Integer findTotals() {
        return provinceDao.findTotals();
    }

    //添加省份
    @Override
    public void save(Province province) {
        province.setPlacecounts(0);//新省份添加时景点数为0
        provinceDao.save(province);
    }

    //删除省份
    @Override
    public void delete(String id) {
        provinceDao.delete(id);
    }

    //查询一个省份
    @Override
    public Province findOne(String id) {
        return provinceDao.findOne(id);
    }
    
    /**
     * 通过名称查询
     * @param name
     * @return
     */
    @Override
    public List<Province> findByName(String name) {
        return provinceDao.findByName(name);
    }

    //修改省份
    @Override
    public void update(Province province) {
        provinceDao.update(province);
    }


}


  • ProvinceController.java
package com.travel.travels.controller;

import com.travel.travels.entity.Province;
import com.travel.travels.entity.Result;
import com.travel.travels.service.ProvinceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@CrossOrigin
@RequestMapping("/province")
public class ProvinceController {
    @Autowired
    ProvinceService provinceService;

    @GetMapping("/findByPage")
    public Map<String,Object> findByPage(Integer page, Integer rows){
        page = page==null? 1 : page;
        rows = rows==null? 4 : rows;
        System.out.println(page+ " : " + rows);
        HashMap<String,Object> map = new HashMap<>();

        //分页查询出当前页面显示的数据
        List<Province> provinces = provinceService.findByPage(page,rows);

        //查询总数据条数
        Integer totals = provinceService.findTotals();
        // 计算总页数
        // 如果总数据条数可以整除每一页数据个数, 说明结果正好为总页数
        // 如果总数据条数无法整除每一页数据个数, 说明总页数需要结果 + 1
        Integer totalPage = totals % rows == 0 ? totals/rows : totals/rows + 1;

        map.put("provinces",provinces);
        map.put("page",page);
        map.put("totalPage",totalPage);
        map.put("totals",totals);

        map.forEach((k, v) -> {
            System.out.println(k + ": " + v);
        });

        return map;

    }

    /**
     * 添加省份
     * @param province
     * @return
     */
    @PostMapping("/save")
    public Result save(@RequestBody Province province){
        Result result = new Result();
        try{
            provinceService.save(province);
            result.setMsg("添加省份成功");
        }catch (Exception e){
            e.printStackTrace();
            result.setState(false).setMsg("添加省份失败!");
        }
        return result;
    }

    /**
     * 删除省份
     * @param id
     * @return
     */
    @GetMapping("/delete")
    public Result delete(String id){
        Result result = new Result();
        try{
            provinceService.delete(id);
            result.setMsg("删除省份成功");
        }catch (Exception e){
            e.printStackTrace();
            result.setState(false).setMsg("删除省份失败!");
        }
        return result;
    }

    /**
     * 修改省份
     * @param province
     * @return
     */
    @PostMapping("/update")
    public Result update(@RequestBody Province province){
        Result result = new Result();
        try{
            provinceService.update(province);
            result.setMsg("修改省份成功!");
        }catch (Exception e){
            e.printStackTrace();
            result.setState(false).setMsg("修改省份失败");
        }
        return result;
    }

    /**
     * 查询一个省份
     * @param id
     * @return
     */
    @GetMapping("/findOne")
    public Province findOne(String id){
        return provinceService.findOne(id);
    }


    /**
     * 通过名称查询
     * @param name
     * @return
     */
    @GetMapping("/findByName")
    public List<Province> findByName(String name){
        return  provinceService.findByName(name);
    }
}

5、省份增删改查及分页前端开发

  • 新建组件main.vue
<template>
   <div>
    <div id="wrap">
        <div id="header">
            <div style="float: right;padding-top: 24px">
                {{time}} &emsp;
                <a @click="back_login" style="color:#fff;float: right">安全退出</a>
            </div>
            <h1>旅游信息管理系统</h1>
        </div>
        <div id="header-bar"></div>
        <div id="content" style="height: 360px">
            <h2>省份列表</h2>
            <input v-model="pname" @keyup.enter="search_ByPname" type="text" placeholder="请输入查询条件">
            <table>
                <thead>
                <tr>
                    <th width="15%">ID</th>
                    <th width="20%">省份</th>
                    <th width="25%">标签</th>
                    <th width="15%">景点个数</th>
                    <th width="25%">操作</th>
                </tr>
                </thead>
                <tbody>
                <tr v-for="province in provinces" :key="province.id">
                    <td v-text="province.id"></td>
                    <td v-text="province.name"></td>
                    <td v-text="province.tags"></td>
                    <td v-text="province.placecounts"></td>
                    <td>
                        <a href="javascript:;" @click="deleteProvince(province.id)">删除省份</a>
                        <a :href="'../viewspot/viewspotlist.html?id=' + province.id">景点列表</a>
                        <!-- <a :href="'../update_province?id=' + province.id">修改省份</a> -->
                        <a  href="javascript:;" @click="updateProvince(province.id)">修改省份</a>
                    </td>
                </tr>
                </tbody>
            </table>
            <a @click="to_addprovince">
                <button type="button">添加省份</button>
            </a>
            <div id="pages">
                <!--上一页, 只有当前所在页数>1才会显示-->
                <a href="javascript:;" class="page" v-if="page > 1" @click="findAll(page - 1)">&lt;上一页</a>
                <!--页面-->
                <a href="javascript:;" class="page" v-for="indexpage in totalPage" @click="findAll(indexpage)"
                   v-text="indexpage"></a>
                <!--下一页, 只有当前所在页数<总页数才会显示-->
                <a href="javascript:;" class="page" v-if="page < totalPage" @click="findAll(page + 1)">下一页&gt;</a>
            </div>
        </div>
        <div id="footer">
            yusael~
        </div>
    </div>
   </div>
</template>

<script>
    export default {
        name: "Main",
        data() {
            return {
                provinces: [],
                page: 1,
                rows: 4,
                totalPage: 0,
                totals: 0,
                time: "",
                pname:'广东',
            }
        },
        methods: {
            search_ByPname(){
               const _this = this
               this.axios.get("/province/findByName?name="+ this.pname ).then(res =>{
                    console.log(res)
                    _this.provinces = res.data;
               }) 
            },
            to_addprovince(){
                this.$router.push("/add_province")
            },
            findAll(indexpage) { // 查询某一页的数据
                if (indexpage) {
                    this.page = indexpage;
                }
                const _this = this; // 保存当前对象, 用于下面的作用域
                this.axios.get("/province/findByPage?page=" + this.page + "&rows=" + this.rows).then((res) => {
                    console.log(res)
                    _this.provinces = res.data.provinces;
                    _this.page = res.data.page;
                    _this.totalPage = res.data.totalPage;
                    _this.totals = res.data.totals;
                });
            },
            deleteProvince(id) {
                if (confirm("确定要删除省份信息吗?")) {
                    this.axios.get("/province/delete?id=" + id).then((res) => {
                        if (res.data.state) {
                            alert(res.data.msg + "点击确定跳转到省份列表页面!");
                            location.reload(true); // 刷新当前页面
                        } else {
                            alert(res.data.msg);
                        }
                    });
                }
            },
            updateProvince(id){
                this.$router.push("/update_province?id="+ id);
            },
            back_login(){
                this.$router.push("/login")
            },
        },
        created() {
            this.findAll();
            let now = new Date();
            this.time = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
        }
    }
</script>

<style scoped>
  table {
            width: 100%;
            margin-bottom: 15px;
            border-collapse: collapse;
            table-layout: fixed;
        }

        th, td {
            border: 1px solid #CBD6DE;
            padding-left: 10px;
            line-height: 28px;
        }

        th {
            text-align: left;
            background: linear-gradient(#edf2f5, #dce9f2, #edf2f5);
            color: #467aa7;
        }

        tbody tr:nth-child(even) {
            background: #f2f2f2;
        }

        #pages {
            text-align: center;
            padding-top: 8px;
        }

        .page {
            min-width: 50px;
            display: inline-block;
        }
</style>



  • add_province.vue
<template>
    <div>
        <div id="wrap">
            <div id="header">
                <div style="float: right;padding-top: 24px">{{time}} &emsp;</div>
                <h1>旅游信息管理系统</h1>
            </div>
            <div id="header-bar"></div>
            <div id="content" style="height: 360px">
                <img  style="float: right;height: 320px">
                <h2>添加省份</h2>
                <form action="provincelist.html" method="post">
                    <label>
                        <div class="label-text">&emsp;份:</div>
                        <input type="text" v-model="province.name">
                    </label>
                    <label>
                        <div class="label-text">&emsp;签:</div>
                        <input type="text" v-model="province.tags">
                    </label>
                    <button type="button" @click="saveProvinceInfo">提 交</button>&emsp;
                    <a @click="back_main">返回</a>
                </form>
            </div>
            <div id="footer">
                yusael~
            </div>
        </div>
    </div>
</template>

<script>
    export default{
        data() {
            return {
                province: {},
                 time: "",
            }
        },
        methods: {
            saveProvinceInfo() {
                const _this = this
                this.axios.post("/province/save", this.province).then((res) => {
                    if (res.data.state) {
                        alert(res.data.msg + "点击确定跳转到省份列表页面!");
                        _this.$router.push('/main')
                    } else {
                        alert(res.data.msg);
                    }
                });
            },
            back_main(){
                this.$router.push("/main")
            }
        },
        created() {
            let now = new Date();
            this.time = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
        }
    }
</script>

<style scoped> 
 form {
            width: 270px;
        }

        input {
            width: 70%;
            background: #eee;
        }

        input:focus {
            background: #fff;
        }

        form {
            padding: 0 12px 12px;
        }

        label {
            display: block;
            padding-bottom: 12px;
        }

        .label-text {
            width: 30%;
            float: left;
        }
</style>
  • update_province.vue
<template>
    <div>
        <div id="wrap">
            <div id="header">
                <div style="float: right;padding-top: 24px">{{time}}&emsp;</div>
                <h1>旅游信息管理系统</h1>
            </div>
            <div id="header-bar"></div>
            <div id="content" style="height: 360px">
                <img  style="float: right;height: 320px">
                <h2>修改省份</h2>
                <form action="provincelist.html" method="post">
                    <label>
                        <div class="label-text">&emsp;份:</div>
                        <input type="text" v-model="province.name">
                    </label>
                    <label>
                        <div class="label-text">&emsp;签:</div>
                        <input type="text" v-model="province.tags">
                    </label>
                    <button type="button" @click="updateProvince">提 交</button>&emsp;
                    <a @click="back_main">返回</a>
                </form>
            </div>
            <div id="footer">
                yusael
            </div>
        </div>

    </div>
</template>

<script>
    export default{
        data() {
            return {
                id: "",
                province: {},
                time: "",
            }
        },
        methods: {
            findOneProvince() {
                const _this = this
                this.axios.get("/province/findOne?id=" + this.id).then((res) => {
                    _this.province = res.data;
                });
            },
            updateProvince() {
                const _this = this
                this.axios.post("/province/update", this.province).then((res) => {
                    if (res.data.state) {
                        alert(res.data.msg + "点击确定跳转到省份列表页面!");
                        _this.$router.push("/main")
                    } else {
                        alert(res.data.msg);
                    }
                });
            },
            back_main(){
                this.$router.push("/main")
            },
        },
        created() {
            this.id = location.href.substring(location.href.indexOf("=") + 1);
            this.findOneProvince();

            let now = new Date();
            this.time = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
        }
    }
</script>

<style scoped>
   form {
            width: 270px;
        }

        input {
            width: 70%;
            background: #eee;
        }

        input:focus {
            background: #fff;
        }

        form {
            padding: 0 12px 12px;
        }

        label {
            display: block;
            padding-bottom: 12px;
        }

        .label-text {
            width: 30%;
            float: left;
        }
</style>
  • 在main.js中导入组件并配置路由
import Main from '../components/Main'
import Add_province from '../components/province/Add_province'
import Update_province from '../components/province/Update_province'

//配置导出路由
export default new VueRouter({
  routes:[
    {
      path: '/main',
      name: Main,
      component: Main
    },
    {
      path: '/add_province',
      name: Add_province,
      component: Add_province
    },
    {
      path: '/update_province',
      name: Update_province,
      component: Update_province
    },
  ]
})

6、效果

完成了功能,页面后续有空再作调整…

省份列表展示:
springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第13张图片
添加省份:
springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第14张图片
删除省份:
springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第15张图片
修改省份:
springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第16张图片
模糊查询:
springboot+mybatis+vue实战——旅游网站(一) 完成登录注册、验证码、省份增删改查功能_第17张图片


推荐文章:
springboot整合redis并在项目中使用
vue+springboot 登录注册功能

你可能感兴趣的:(前后端项目,spring,boot,vue.js)