写在前面:欢迎各位博主前来阅读我的博客,希望通过本篇博客你可以有所收获。如有任何问题,欢迎小伙伴们在评论区评论。由于本人水平有限,如有不足,请您指正,谢谢!

想必小伙们通过博客的标题也看到了,这是一个关于Spring Boot+Vue的前后端分离旅游信息管理系统的实战项目。好了,话不多说,我们先看简单看一下界面的效果吧!
在这里插入图片描述
在这里插入图片描述

前置知识准备:

①前端技术栈:Vue、nodejs
②后端技术栈: Spring Boot + MyBatis
③前后端通信方式:axios json

下面我将从需求分析→库表设计→编码(项目环境搭建+项目编码) →项目调试实现一个完整的项目。

一、需求分析

1、用户模块:登录和注册

2、省份模块:CRUD(增删改查)

3、景点模块:CRUD(增删改查)

二、库表设计

在创建表之前,我们先需要创建数据库,使用以下语句即可。

CREATE DATABASE travel DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

好啦,数据库已经建立好啦,接下来我们来建立用户表、省份表和景点表吧。

-- 用户表 
create table t_user(
	id int(6) PRIMARY key auto_increment,
	username varchar(60),
	password varchar(60),
	email varchar(60)
);


-- 省份表
create table t_province(
	id int(6) PRIMARY key auto_increment,
	name varchar(60),
	tags varchar(80),
	placecounts int(4)
);

-- 景点表
create table t_place(
	id int(6) PRIMARY key auto_increment,
	name varchar(60),
	picpath varchar(100),
	hottime TIMESTAMP,
	hotticket double(7,2),
	dimticket double(7,2),
	placedes varchar(300),
	provinceid int(6) REFERENCES t_province(id)
);

三、项目编码

我们先来看一下项目的整体结构吧!
在这里插入图片描述

1、环境搭建

(1)导入环境所需的依赖

<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.2</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>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </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>

(2)application.yml项目配置信息

#端口号配置
server:
  port: 8080

#数据源配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/travels?characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource

  #静态资源路径
  resources:
    static-locations: file:${upload.dir}

  servlet:
    multipart:
      # 设置单个文件的大小
      max-file-size: 50MB
      max-request-size: 50MB

#mybatis相关配置
mybatis:
  #配置MyBatis的xml配置文件路径
  mapper-locations: classpath:com/uos/travels/mapper/*.xml
  #配置XML映射文件中指定的实体类别名路径
  type-aliases-package: com.uos.travels.entity

# 配置日志等级
logging:
  level:
    root: info
    com.uos.travels.dao: debug


# 文件上传配置
upload:
  dir: E:/travels/images

(3)静态资源导入

静态资源gitee地址,请点击该链接访问
在这里插入图片描述
把下载后的资源放入到static目录下。你可以直接下载zip压缩包,当然你也可以使用命令
git clone https://gitee.com/chuzhuyong/travels_static.git将资源克隆到本地。

2、用户模块开发

(1)用户注册+用户登录 后端处理

①在entity包下新建User实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String id;
    private String username;
    private String password;
    private String email;
}

此时,我们也新建一个实体类Result,该类主要用于controller中返回的结果对象。

@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Result {
    private Boolean state = true;
    private String message;
    private String userId;
}

在这里,有的小伙伴们可能就有疑问了。我之前倒是使用过lombok的@Data、@NoArgsConstructor、@AllArgsConstructor注解,但是这里的@Accessors(chain = true)注解有啥作用嘞?

举个栗子吧,请看下面代码的区别。

没有使用@Accessors(chain = true) 之前的set方法是这样的。

public void setMessage(String message) {
        this.message = message;
}

使用@Accessors(chain = true) 之后的set方法是这样的。

public Result setMessage(final String message) {
        this.message = message;
        return this;
}

到这里,小伙伴们就可以看出区别了。使用@Accessors(chain = true)返回值为this,使用该注解的好处主要是方便级联操作。

②在utils包下新建图片验证码类
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();
    }
}

小伙伴们,这是一个非常好用的创建验证码的工具类,以后可以复用的呦。

③在dao包下新建UserDao类
@Mapper
public interface UserDao {
    // 注册用户
    void save(User user);
    // 根据用户名查询用户
    User findByUsername(String username);
}

温馨提示:这里的@Mapper注解不要忘记添加,它的功能是把mapper这个DAO交给Spring管理。

④在mapper目录下新建userDao.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.uos.travels.dao.UserDao">
    <!--保存用户-->
    <insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values (#{id},#{username},#{password},#{email})
    </insert>

    <!--根据用户名查询用户-->
    <select id="findByUsername" resultType="User" parameterType="String">
        select * from t_user where username=#{username}
    </select>
</mapper>
⑤在service包下新建UserService接口
public interface UserService {
    // 注册
    void register(User user);
    // 登录
    User login(User user);
}
⑥在service包下创建impl包,并新建UserServiceImpl类
@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 (user.getPassword().equals(userDB.getPassword())) {
                return userDB;
            }
            throw new RuntimeException("密码错误");
        }else {
            throw new RuntimeException("用户名错误");
        }
    }
}
⑦在controller包下新建UserController类
@RestController
@RequestMapping("/user")
@CrossOrigin    // 允许跨域
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 注册
     */
    @PostMapping("/register")
    public Result register(String code, String key, @RequestBody User user, HttpServletRequest request) {
        Result result = new Result();

        log.info("接收到的code:"+code);
        log.info("接收到的user:"+user);
        String keyCode = (String) request.getServletContext().getAttribute(key);
        log.info("接收到的key:"+keyCode);
        try {
            if (code.equalsIgnoreCase(keyCode)) {
                // 注册用户
                userService.register(user);
                result.setMessage("注册成功");
                result.setState(true);
            }else {
                throw new RuntimeException("验证码错误");
            }
        }catch (Exception e) {
            e.printStackTrace();
            result.setMessage(e.getMessage()).setState(false);
        }
        return result;
    }

    /**
     * 登录
     */
    @RequestMapping("/login")
    public Result login(@RequestBody User user,HttpServletRequest request) {
        Result result = new Result();
        log.info("user:"+user);

        try {
            User userDB = userService.login(user);
            // 登录成功之后保存用户的标记
            request.getServletContext().setAttribute(userDB.getId(),userDB);
            result.setMessage("登录成功").setUserId(userDB.getId());
        } catch (Exception e) {
            e.printStackTrace();
            result.setMessage(e.getMessage()).setState(false);
        }


        return result;
    }


    /**
     * 获取验证码
     */
    @GetMapping("/getImage")
    public Map<String, String> getImage(HttpServletRequest request) throws IOException {
        Map<String,String> result = new HashMap<>();
        CreateImageCode createImageCode = new CreateImageCode(120,40,4,10);
        // 获取验证码
        String code = createImageCode.getCode();
        // 将验证码存入session
        /*session.setAttribute("imageCode",code);*/
        String key = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        request.getServletContext().setAttribute(key,code);
        // 生成图片
        BufferedImage img = createImageCode.getBuffImg();
       // 进行base64编码
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ImageIO.write(img,"png",bos);
        String string = Base64Utils.encodeToString(bos.toByteArray());
        result.put("key",key);
        result.put("image",string);
        return result;
    }
}

小伙伴们注意一下,上述的代码是包含了注册和登录两个功能的呦。下面我们就集中精力处理一下用户注册和登录的前端操作。

(2)用户注册+用户登录 前端处理

①用户注册前端处理
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="css/style.css">
    <style>
        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>
</head>
<body>
<div id="app">
    <div id="wrap">
        <div id="header">
            <div style="float: right;padding-top: 24px">
                <!--时间显示区域-->
                <span id="show">
                </span>
            </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" :key="key" @click="changeImage">
                <label>
                    <div class="label-text">验证码:</div>
                    <input type="text" v-model="vcode" name="vcode" style="width: 100px">
                </label>
                <button type="button" @click="saveUserInfo">提 交</button>&emsp;
                <a href="login.html">去登录</a>
            </form>
        </div>
        <div id="footer">
            eternalreminisce@126.com
        </div>
    </div>
</div>

<script src="js/vue.js"></script>
<script src="js/axios.min.js"></script>
<script>
    var app = new Vue({
        el: "#app",
        data: {
            user: {},
            vcode: "",
            src: "",
            key: ""
        },

        // 注册
        methods: {
            saveUserInfo(){
                /*console.log(this.user);*/
                console.log("验证码"+this.vcode);
                if (!this.user.username) {
                    alert("用户名不能为空");
                }
                if (!this.user.password) {
                    alert("密码不能为空");
                }
                if (!this.user.email) {
                    alert("邮箱不能为空");
                }

                //发送axios
                axios.post("http://localhost:8080/user/register?code="+this.vcode+"&key="+this.key,this.user).then((res=>{
                    console.log(res.data);
                    if (res.data.state) {
                        // 注册成功
                        alert(res.data.message+",点击确定跳转到登录页面");
                        location.href="login.html";
                    }else {
                        alert(res.data.message);
                    }
                }));
            },
            // 获取验证码
            getImage(){
                _this = this;
                axios.get("http://localhost:8080/user/getImage").then((res)=>{
                    console.log(res.data);
                    // 展示图片进行解码
                    _this.src = "data:image/png;base64," + res.data.image;
                    _this.key = res.data.key;
                })

            },
            // 切换验证码
            changeImage(){
                this.getImage();
            }
        },
        created(){
            // 获取验证码
            this.getImage();
        }
    })
</script>
<script src="js/date.js"></script>
</body>
</html>
②用户登录前端处理
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="css/style.css">
    <style>
        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>
</head>
<body>
<div id="app">
    <div id="wrap">
        <div id="header">
            <div style="float: right;padding-top: 24px">
                <!--时间显示区域-->
                <span id="show">
                </span>
            </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" name="username" v-model="user.username">
                </label>
                <label>
                    <div class="label-text">&emsp;码:</div>
                    <input type="password" name="password" v-model="user.password">
                </label>
                <!--<img src="img/vcode.png" id="img-vcode">
                <label>
                    <div class="label-text">验证码:</div>
                    <input type="text" name="vcode" style="width: 100px">
                </label>-->
                <button type="button" @click="login">提 交</button>&emsp;
                <a href="reg.html">去注册</a>
            </form>
        </div>
        <div id="footer">
            eternalreminisce@126.com
        </div>
    </div>
</div>
    <script src="js/vue.js"></script>
    <script src="js/axios.min.js"></script>
    <script>
        var app = new Vue({
            el: "#app",
            data:{
                user:{},
            },
            methods:{
                // 登录
                login(){
                    // 发送axios
                    axios.post("http://localhost:8080/user/login",this.user).then((res)=>{
                        console.log(res.data);
                        localStorage.setItem("userId",res.data.userId);

                        if (res.data.state) {
                            // 登录成功
                            alert(res.data.message+",点击确定进入主页");
                            location.href="province/provincelist.html";
                        }else {
                            alert(res.data.message);
                        }
                    });
                }
            }
        })
    </script>
<script src="js/date.js"></script>
</body>
</html>

(3)用户注册和用户登录效果演示

在这里插入图片描述
注册中的验证码是可以点击切换的呦(验证码不区分大小写)!

3、省份模块开发

(1)在entity包下新建Province实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Province {
    private String id;
    private String name;
    private String tags;
    private Integer placeCounts;
}

(2)在dao包下新建ProvinceDao接口

public interface BaseDao<T,K> {
    void save(T t);

    void delete(K k);

    void update(T t);

    T findOne(K k);

    List<T> findAll();

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

    Integer findTotals();
    
}
@Mapper
public interface ProvinceDao extends BaseDao<Province,String> {
}

(3)在mapper下新建provinceDao.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.uos.travels.dao.ProvinceDao">


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

    <!--查询总条数-->
    <select id="findTotals" resultType="integer">
        select count(id) from t_province
    </select>

    <!--省份添加-->
    <insert id="save" parameterType="Province" useGeneratedKeys="true" keyProperty="id">
        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 * 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>

(4)在service包下新建ProvinceService接口

public interface ProvinceService {
    /**
     *start:当前页
     * counts:每页显示的条数
     */
    List<Province> findByPage(Integer page,Integer rows);

    /**
     * 查询总条数
     */
    Integer findTotals();

    /**
     * 保存省份
     */
    void save(Province province);

    /**
     * 删除省份
     */
    void delete(String id);

    /**
     * 查询省份信息
     */
    Province findOne(String id);

    /**
     * 更新省份信息
     */
    void update(Province province);
}
@Service
@Transactional
public class ProvinceServiceImpl implements ProvinceService {

    @Autowired
    private ProvinceDao provinceDao;

    @Override
    public List<Province> findByPage(Integer page, Integer rows) {
        int start = (page - 1)*rows;
        return provinceDao.findByPage(start,rows);
    }

    @Override
    public Integer findTotals() {
        return provinceDao.findTotals();
    }

    @Override
    public void save(Province province) {
        province.setPlaceCounts(0);
        provinceDao.save(province);
    }

    @Override
    public void delete(String id) {
        provinceDao.delete(id);
    }

    @Override
    public Province findOne(String id) {
        return provinceDao.findOne(id);
    }

    @Override
    public void update(Province province) {
        provinceDao.update(province);
    }
}

(5)在controller包下新建ProvinceController

@RestController
@CrossOrigin    // 允许跨域
@RequestMapping("/province")
public class ProvinceController {

    @Autowired
    private ProvinceService provinceService;

    /**
     * 查询所有
     */
    @RequestMapping(value = "/findByPage",method = RequestMethod.GET)
    public Map<String,Object> findByPage(Integer page,Integer rows){
        // 如果为空指定默认值
        page = page==null?1:page;
        rows = rows==null?4:rows;

        Map<String, Object> map = new HashMap<>();
        // 分页处理
        List<Province> provinces = provinceService.findByPage(page, rows);
        // 计算总页数
        Integer totals = provinceService.findTotals();
        Integer totalPage = totals%rows==0?totals/rows:totals/rows+1;
        map.put("provinces",provinces);
        map.put("totals",totals);
        map.put("totalPage",totalPage);
        map.put("page",page);
        return map;
    }

    /**
     * 省份信息添加
     */
    @PostMapping("/save")
    public Result save(@RequestBody Province province) {
        Result result = new Result();
        try {
            provinceService.save(province);
            result.setMessage("保存省份信息成功");
        } catch (Exception e) {
            e.printStackTrace();
            result.setMessage("保存省份信息失败").setState(false);
        }
        return result;
    }
    /**
     * 省份信息删除
     */
    @GetMapping("/delete")
    public Result delete(String id) {
        Result result = new Result();
        try {
            provinceService.delete(id);
            result.setMessage("删除省份信息成功");
        } catch (Exception e) {
            e.printStackTrace();
            result.setMessage("删除省份信息失败").setState(false);
        }
        return result;
    }

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

    /**
     * 修改省份信息
     */
    @PostMapping("/update")
    public Result update(@RequestBody Province province) {
        Result result = new Result();
        try {
            provinceService.update(province);
            result.setMessage("修改省份信息成功");

        } catch (Exception e) {
            e.printStackTrace();
            result.setState(false).setMessage(e.getMessage());
        }
        return result;
    }
}

(6)前端页面

添加省份页面

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="../css/style.css">
    <style>
        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>
</head>
<body>
<div id="app">
    <div id="wrap">
        <div id="header">
            <div style="float: right;padding-top: 24px">
                <!--时间显示区域-->
                <span id="show">
                </span></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="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 href="provincelist.html">返回</a>
            </form>
        </div>
        <div id="footer">
            eternalreminisce@126.com
        </div>
    </div>
</div>
    <script src="../js/vue.js"></script>
    <script src="../js/axios.min.js"></script>
    <script>
        var app = new Vue({
            el: "#app",
            data:{
                province:{},
            },
            methods:{
                // 保存省份信息
                saveProvinceInfo(){
                  /*  if (!this.province.name) {
                        alert("省份名称不能为空");
                    }
                    if (!this.province.tags) {
                        alert("省份标签不能为空");
                    }*/

                    // 发送axios
                    axios.post("http://localhost:8080/province/save",this.province).then((res)=>{
                        console.log(res.data);
                        if (res.data.state) {
                            // 保存省份信息成功
                            alert(res.data.message+",点击确定跳转到省份列表页面");
                            location.href="provincelist.html";
                        }else {
                            alert(res.data.message);
                        }
                    });
                }
            }
        })
    </script>
<script src="../js/date.js"></script>
</body>
</html>

省份列表页面

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="../css/style.css">
    <style>
        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: center;
            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>
    <script src="../js/date.js"></script>
</head>
<body>
<div id="app">
<div id="wrap">
    <div id="header">
        <div style="float: right;padding-top: 24px">
            <!--时间显示区域-->
            <span id="show">
            </span>
            <a href="../login.html" style="color:#fff;float: right">安全退出</a>
        </div>
        <h1>旅游信息管理系统</h1>
    </div>
    <div id="header-bar"></div>
    <div id="content" style="height: 360px">
        <h2>省份列表</h2>
        <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" align="center">
                <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="'./updateprovince.html?id='+province.id">修改省份</a>
                </td>
            </tr>
            </tbody>
        </table>
        <a href="addprovince.html">
            <button type="button">添加省份</button>
        </a>
        <div id="pages">
            <a href="javascript:;" class="page" @click="findAll(page-1)" v-if="page>1">&lt;上一页</a>
            <a href="javascript:;" class="page" @click="findAll(page-1)" v-if="page==1" disabled="">&lt;上一页</a>
            <a href="javascript:;" class="page" v-for="indexpage in totalPage" v-text="indexpage" @click="findAll(indexpage)"></a>
            <a href="javascript:;" class="page" v-if="page==totalPage">下一页&gt;</a>
            <a href="javascript:;" class="page" v-if="page!=totalPage" @click="findAll(page+1)">下一页&gt;</a>
        </div>
    </div>
    <div id="footer">
        eternalreminisce@126.com
    </div>
</div>
</div>

<script src="../js/vue.js"></script>
<script src="../js/axios.min.js"></script>
<script>
    var app = new Vue({
        el: "#app",
        data:{
            provinces:[],
            page:1,
            rows:4,
            totalPage:0,
            totals:0,
            id:0
        },
        methods:{
            // 查询所有
            findAll(indexpage){
                if (indexpage){
                    this.page=indexpage;
                }
                _this = this;
                // 发送axios
                axios.get("http://localhost:8080/province/findByPage?page="+this.page).then((res)=>{
                    _this.provinces = res.data.provinces;
                    _this.page = res.data.page;
                    _this.rows = res.data.rows;
                    _this.totalPage = res.data.totalPage;
                    _this.totals = res.data.totals;
                });
            },
            // 删除省份信息
            deleteProvince(id){
                if (confirm("确定要删除省份信息吗?")){
                    axios.get("http://localhost:8080/province/delete?id="+id).then((res)=>{
                        if (res.data.state) {
                            // 删除省份信息成功
                            alert(res.data.message+",点击确定跳转到省份列表页面");
                            // 刷新当前页面
                            location.reload(true);
                        }else {
                            alert(res.data.message);
                        }
                    })
                }
            }
        },
        created(){
            this.findAll();
        }
    })
</script>
<script src="../js/date.js"></script>
</body>
</html>

更新省份列表页面

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="../css/style.css">
    <style>
        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>
</head>
<body>
<div id="app">
    <div id="wrap">
        <div id="header">
            <div style="float: right;padding-top: 24px">
                <!--时间显示区域-->
                <span id="show">
                </span>
            </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="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 href="provincelist.html">返回</a>
            </form>
        </div>
        <div id="footer">
            eternalreminisce@126.com
        </div>
    </div>
</div>
    <script src="../js/vue.js"></script>
    <script src="../js/axios.min.js"></script>
    <script>
        var app = new Vue({
            el: "#app",
            data:{
                id:"",
                province:{}
            },
            methods:{
                findOneProvince(id){
                    _this = this;
                    axios.get("http://localhost:8080/province/findOne?id="+id).then((res)=>{
                        console.log(res.data);
                        _this.province = res.data;
                    });
                },
                updateProvince() {
                    axios.post("http://localhost:8080/province/update",this.province).then((res)=>{
                        if (res.data.state) {
                            // 修改省份信息成功
                            alert(res.data.message+",点击确定跳转到省份列表页面");
                            location.href="provincelist.html";
                        }else {
                            alert(res.data.message);
                        }
                    });
                }
            },
            created(){
                this.id = location.href.substring(location.href.indexOf("=")+1);
                this.findOneProvince(this.id);
            }
        })
    </script>
<script src="../js/date.js"></script>
</body>
</html>

(7)效果演示

在这里插入图片描述

4、景点模块开发

(1)创建Place实体类

package com.uos.travels.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.Date;

/**
 * @author 太子殿下
 * @date 2020/6/1 18:49
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Place {
    private String id;
    private String name;
    private String picPath;
    @JsonFormat(pattern = "yyyy/MM/dd")
    private Date hotTime;
    private Double hotTicket;
    private Double dimTicket;
    private String placeDes;
    private String provinceId;
}

(2)创建PlaceDao 接口

@Mapper
public interface PlaceDao extends BaseDao<Place,String> {

    /**
     * 分页查询
     */
    List<Place> findByProvinceIdPage(@Param("start") Integer start, @Param("rows") Integer rows,@Param("provinceId") String provinceId);

    /**
     * 根据id查询省份的个数
     */
    Integer findByProvinceIdCounts(String id);

    /**
     *保存景点信息
     */
    void save(Place place);
    
}

(3)编写placeDao.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.uos.travels.dao.PlaceDao">

    <!--根据省份id查询查询所有景点并排序-->
    <select id="findByProvinceIdPage" resultType="Place">
        select * from t_place where provinceid = #{provinceId} order by hotticket limit #{start},#{rows}
    </select>

    <!--根据省份id查询当前省份景点个数-->
    <select id="findByProvinceIdCounts" resultType="Integer" parameterType="String">
        select count(id) from t_place where provinceid = #{provinceId}
    </select>

    <!--保存景点信息-->
    <insert id="save" parameterType="Place" useGeneratedKeys="true" keyProperty="id">
        insert into t_place values(#{id},#{name},#{picPath},#{hotTime},#{hotTicket},#{dimTicket},#{placeDes},#{provinceId})
    </insert>

    <!--根据id查询景点信息-->
    <select id="findOne" parameterType="String" resultType="Place">
        select * from t_place where id = #{id}
    </select>

    <!--根据id删除景点信息-->
    <delete id="delete" parameterType="String">
        delete from t_place where id = #{id}
    </delete>

    <!--修改景点信息-->
    <update id="update" parameterType="Place">
        update t_place set
            name = #{name},
            picpath = #{picPath},
            hottime = #{hotTime},
            hotticket = #{hotTicket},
            dimticket = #{dimTicket},
            placedes = #{placeDes},
            provinceid = #{provinceId}
        where id = #{id}
    </update>

</mapper>

(4)编写PlaceService接口

public interface PlaceService {
    List<Place> findByProvinceIdPage(Integer page, Integer rows, String provinceId);

    Integer findByProvinceIdCounts(String id);

    void save(Place place);

    void delete(String id);

    void update(Place place);

    Place findOne(String id);
}
@Service
@Transactional
public class PlaceServiceImpl implements PlaceService {

    @Autowired
    private PlaceDao placeDao;
    
    @Autowired
    private ProvinceService provinceService;

    @Override
    public List<Place> findByProvinceIdPage(Integer page, Integer rows, String provinceId) {
        int start = (page - 1)*rows;
        return placeDao.findByProvinceIdPage(start,rows,provinceId);
    }

    @Override
    public Integer findByProvinceIdCounts(String id) {
        return placeDao.findByProvinceIdCounts(id);
    }

    @Override
    public void save(Place place) {
        placeDao.save(place);
        // 查询省份信息
        Province province = provinceService.findOne(place.getProvinceId());
        // 更新省份景点个数
        province.setPlaceCounts(province.getPlaceCounts()+1);
        provinceService.update(province);
    }

    @Override
    public void delete(String id) {
        Place place = placeDao.findOne(id);
        Province province = provinceService.findOne(place.getProvinceId());
        province.setPlaceCounts(province.getPlaceCounts()-1);
        // 更新景点信息,将当前省份下景点的个数减1
        provinceService.update(province);
        // 删除景点信息
        placeDao.delete(id);
    }

    @Override
    public void update(Place place) {
        placeDao.update(place);
    }

    @Override
    public Place findOne(String id) {
        return placeDao.findOne(id);
    }
}

(5)编写PlaceController类

@RestController
@CrossOrigin    // 允许跨域
@RequestMapping("/place")
public class PlaceController {

    @Autowired
    private PlaceService placeService;

    @Value("${upload.dir}")
    private String realPath;

    /**
     * 查询所有
     */
    @GetMapping(value = "/findAllPage")
    public Map<String,Object> findAllPage(Integer page, Integer rows,String provinceId){
        // 如果为空指定默认值
        page = page==null?1:page;
        rows = rows==null?3:rows;

        Map<String, Object> result = new HashMap<>();
        // 景点集合
        List<Place> places = placeService.findByProvinceIdPage(page,rows,provinceId);
        // 处理分页
        Integer counts = placeService.findByProvinceIdCounts(provinceId);
        // 总页数
        Integer totalPage = counts%rows==0?counts/rows:counts/rows+1;

        result.put("places",places);
        result.put("counts",counts);
        result.put("totalPage",totalPage);
        result.put("page",page);

        return result;
    }

    /**
     * 保存景点信息
     */
    @PostMapping("/save")
    public Result save(MultipartFile pic, Place place) throws IOException {
        /*System.out.println(pic.getOriginalFilename());*/
        System.out.println(place);
        Result result = new Result();
        try {
            // 文件上传处理
            String extension = FilenameUtils.getExtension(pic.getOriginalFilename());
            String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + extension;
            // bas464编码处理
            place.setPicPath(Base64Utils.encodeToString(pic.getBytes()));
            pic.transferTo(new File(realPath,newFileName));
            // 保存景点对象
            placeService.save(place);
            result.setMessage("保存景点信息成功");
        } catch (Exception e) {
            result.setState(false).setMessage(e.getMessage());
        }
        return result;
    }
    /**
     * 删除景点信息
     */
    @GetMapping("/delete")
    public Result delete(String id){
        Result result = new Result();
        try {
            placeService.delete(id);
            result.setMessage("删除景点信息成功");
        } catch (Exception e) {
            e.printStackTrace();
            result.setState(false).setMessage(e.getMessage());
        }
        return result;
    }
    /**
     * 查询景点信息
     */
    @GetMapping("/findOne")
    public Place findOne(String id){
        return placeService.findOne(id);
    }
    /**
     * 更新景点信息
     */
    @PostMapping("/update")
    public Result update(MultipartFile pic,Place place) throws IOException {
        Result result = new Result();
        try {
            // 对图片进行base64处理
            String picPath = Base64Utils.encodeToString(pic.getBytes());
            place.setPicPath(picPath);

            // 文件上传处理
            String extension = FilenameUtils.getExtension(pic.getOriginalFilename());
            String newFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + extension;
            pic.transferTo(new File(realPath,newFileName));
            //修改景点信息
            placeService.update(place);
            result.setMessage("修改景点信息成功");
        } catch (Exception e) {
            e.printStackTrace();
            result.setState(false).setMessage(e.getMessage());
        }

        return result;
    }
}

(6)前端页面

添加景点页面

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="../css/style.css">
    <style>
        form{
            width:270px;
        }
        input{
            width: 64%;
            background: #eee;
        }
        input:focus{
            background: #fff;
        }
        form{
            padding: 0 12px 12px;
        }
        label{
            display: block;
            padding-bottom:12px;
        }
        .label-text{
            width: 36%;
            float: left;
        }
        #upload-tip{
            border: 1px dashed #d9d9d9;
            width: 135px;
            height: 135px;
            line-height: 135px;
            cursor: pointer;
            font-size: 36px;
            color:#d9d9d9;
        }
        #img-show{
            width: 135px;
            height: 135px;
            display: block;
            margin: 0 auto;
            object-fit: cover;
        }
    </style>
    <script>
        function imgfileChange() {
            var upload_tip = document.getElementById("upload-tip");
            var img_show = document.getElementById("img-show");
            var imgfile = document.getElementById("imgfile");

            var freader = new FileReader();
            freader.readAsDataURL(imgfile.files[0]);
            freader.onload = function (e) {
                img_show.src = e.target.result;
                img_show.style.display = "inline";
                upload_tip.style.display = "none";
            };
        }
    </script>
</head>
<body>
<div id="app">
    <div id="wrap">
        <div id="header">
            <div style="float: right;padding-top: 24px">
                <!--时间显示区域-->
                <span id="show">
                </span>
            </div>
            <h1>旅游信息管理系统</h1>
        </div>
        <div id="header-bar"></div>
        <div id="content" style="height: 480px">
            <img src="../img/timg.jpg" style="float: right;height: 320px">
            <h2>添加景点</h2>
            <form action="viewspotlist.html" method="post">
                <label>
                    <div class="label-text">&emsp;&emsp;点:</div>
                    <input type="text" v-model="place.name">
                </label>
                <label>
                    <div class="label-text">印象图片:</div>
                    <div style="text-align: center;padding-left: 36%">
                        <div id="upload-tip">+</div>
                        <img src="" alt="" id="img-show" style="display: none">
                        <input type="file" id="imgfile" ref="myFile" style="display: none" onchange="imgfileChange()">
                    </div>
                </label>
                <label>
                    <div class="label-text">旺季时间:</div>
                    <input type="text" v-model="place.hotTime">
                </label>
                <label>
                    <div class="label-text">旺季门票:</div>
                    <input type="text" v-model="place.hotTicket">
                </label>
                <label>
                    <div class="label-text">淡季门票:</div>
                    <input type="text" v-model="place.dimTicket">
                </label>
                <label>
                    <div class="label-text">景点描述:</div>
                    <input type="text" v-model="place.placeDes">
                </label>
                <label>
                    <div class="label-text">所属省份:</div>
                    <select v-model="place.provinceId">
                        <option :value="pro.id" v-for="pro in provinces" v-text="pro.name"></option>
                    </select>
                </label>
                <button type="button" @click="savePlaceInfo">提 交</button>&emsp;
                <a :href="'./viewspotlist.html?id='+id">返回</a>
            </form>
        </div>
        <div id="footer">
            eternalreminisce@126.com
        </div>
    </div>
</div>

    <script src="../js/vue.js"></script>
    <script src="../js/axios.min.js"></script>
    <script>
        var app = new Vue({
            el: "#app",
            data:{
                provinces:[],
                place:{},
                id:"",
            },
            methods:{
                findAllProvinces(){     //查询所有景点
                    _this = this;
                    axios.get("http://localhost:8080/province/findByPage?rows=35").then((res)=>{
                        console.log(res.data.provinces);
                        _this.provinces = res.data.provinces;
                    })
                },
                savePlaceInfo() {       //保存所有景点
                    console.log(this.place);
                    let myFile = this.$refs.myFile;
                    let files = myFile.files;
                    let file = files[0];
                    let formData = new FormData();
                    formData.append('pic',file);
                    formData.append('name',this.place.name);
                    formData.append('hotTime',this.place.hotTime);
                    formData.append('hotTicket',this.place.hotTicket);
                    formData.append('dimTicket',this.place.dimTicket);
                    formData.append('placeDes',this.place.placeDes);
                    formData.append('provinceId',this.place.provinceId);
                    //发送axios请求
                    axios({
                        method:'post',
                        url:'http://localhost:8080/place/save',
                        data:formData,
                        headers:{
                            'Content-Type':'multipart/form-data'
                        }
                    }).then((res)=>{
                        console.log(res.data);
                        if (res.data.state) {
                            alert(res.data.message+",点击确定跳转到景点列表");
                            location.href = "./viewspotlist.html?id=" + this.place.provinceId;
                        }else {
                            alert(res.data.message);
                        }
                    })
                }
            },
            created(){
                this.findAllProvinces();
                let id = location.href.substring(location.href.indexOf("=")+1);
                this.id = id;
            }
        })
    </script>
<script src="../js/date.js"></script>
</body>
</html>

更新景点页面

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="../css/style.css">
    <style>
        form{
            width:270px;
        }
        input{
            width: 64%;
            background: #eee;
        }
        input:focus{
            background: #fff;
        }
        form{
            padding: 0 12px 12px;
        }
        label{
            display: block;
            padding-bottom:12px;
        }
        .label-text{
            width: 36%;
            float: left;
        }
        #img-show{
            width: 135px;
            height: 135px;
            display: block;
            margin: 0 auto;
            object-fit: cover;
        }
    </style>
    <script>
        function imgfileChange() {
            var img_show = document.getElementById("img-show");
            var imgfile = document.getElementById("imgfile");

            var freader = new FileReader();
            freader.readAsDataURL(imgfile.files[0]);
            freader.onload = function (e) {
                img_show.src = e.target.result;
            };
        }
    </script>
</head>
<body>
<div id="app">
    <div id="wrap">
        <div id="header">
            <div style="float: right;padding-top: 24px">
                <!--时间显示区域-->
                <span id="show">
                </span>
            </div>
            <h1>旅游信息管理系统</h1>
        </div>
        <div id="header-bar"></div>
        <div id="content" style="height: 480px">
            <img src="../img/timg.jpg" style="float: right;height: 320px">
            <h2>修改景点</h2>
            <form action="viewspotlist.html" method="post">
                <label>
                    <div class="label-text">&emsp;&emsp;点:</div>
                    <input type="text" v-model="place.name">
                </label>
                <label>
                    <div class="label-text">印象图片:</div>
                    <div style="text-align: center;padding-left: 36%">
                        <img :src="src" alt="" id="img-show">
                        <input type="file" id="imgfile" ref="myFile" style="display: none" onchange="imgfileChange()">
                    </div>
                </label>
                <label>
                    <div class="label-text">旺季时间:</div>
                    <input type="text" v-model="place.hotTime">
                </label>
                <label>
                    <div class="label-text">旺季门票:</div>
                    <input type="text" v-model="place.hotTicket">
                </label>
                <label>
                    <div class="label-text">淡季门票:</div>
                    <input type="text" v-model="place.dimTicket">
                </label>
                <label>
                    <div class="label-text">景点描述:</div>
                    <input type="text" v-model="place.placeDes">
                </label>
                <label>
                    <div class="label-text">所属省份:</div>
                    <select v-model="place.provinceId">
                        <option :value="pro.id" v-for="pro in provinces" v-text="pro.name"></option>
                    </select>
                </label>
                <button type="button" @click="updatePlace">提 交</button>&emsp;
                <a :href="'viewspotlist.html?id='+place.provinceId">返回</a>
            </form>
        </div>
        <div id="footer">
            eternalreminisce@126.com
        </div>
    </div>
</div>

    <script src="../js/vue.js"></script>
    <script src="../js/axios.min.js"></script>
    <script>
        var app = new Vue({
            el: "#app",
            data:{
                id:"",
                place:{},
                src:"",
                provinces:[]
            },
            methods:{
                findOnePlace(){
                    _this = this;
                    axios.get("http://localhost:8080/place/findOne?id="+this.id).then((res)=>{
                        _this.place = res.data;
                        _this.src = 'data:image/jpg;base64,'+res.data.picPath;
                    })
                },
                findAllProvinces(){     //查询所有景点
                    _this = this;
                    axios.get("http://localhost:8080/province/findByPage?rows=35").then((res)=>{
                        console.log(res.data.provinces);
                        _this.provinces = res.data.provinces;
                    })
                },
                updatePlace(){
                    console.log(this.place);
                    let myFile = this.$refs.myFile;
                    let files = myFile.files;
                    let file = files[0];
                    let formData = new FormData();
                    formData.append('pic',file);
                    formData.append('id',this.place.id);
                    formData.append('name',this.place.name);
                    formData.append('hotTime',this.place.hotTime);
                    formData.append('hotTicket',this.place.hotTicket);
                    formData.append('dimTicket',this.place.dimTicket);
                    formData.append('placeDes',this.place.placeDes);
                    formData.append('provinceId',this.place.provinceId);
                    //发送axios请求
                    axios({
                        method:'post',
                        url:'http://localhost:8080/place/update',
                        data:formData,
                        headers:{
                            'Content-Type':'multipart/form-data'
                        }
                    }).then((res)=>{
                        console.log(res.data);
                        if (res.data.state) {
                            alert(res.data.message+",点击确定跳转到景点列表");
                            location.href = "./viewspotlist.html?id=" + this.place.provinceId;
                        }else {
                            alert(res.data.message);
                        }
                    })
                }
            },
            created(){
                let id = location.href.substring(location.href.indexOf("=")+1);
                this.id = id;
                this.findOnePlace();
                this.findAllProvinces();
            }
        })
    </script>

<script src="../js/date.js"></script>
</body>
</html>

景点列表页面

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="../css/style.css">
    <style>
        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: center;
            background: linear-gradient(#edf2f5,#dce9f2,#edf2f5);
            color:#467aa7;
        }
        tbody tr:nth-child(4n),tbody tr:nth-child(4n-1){
            background: #f2f2f2;
        }
        #pages{
            text-align: center;
            padding: 8px 0;
        }
        .page{
            min-width: 50px;
            display: inline-block;
        }
        .viewspotimg{
            width: 135px;
            height: 135px;
            margin-left: -10px;
            display: block;
            object-fit: cover;
        }
    </style>
</head>
<body>
<div id="app">
    <div id="wrap">
        <div id="header">
            <div style="float: right;padding-top: 24px">
                <!--时间显示区域-->
                <span id="show">
                </span>
                <a href="../login.html" style="color:#fff;float: right">安全退出</a>
            </div>
            <h1>旅游信息管理系统</h1>
        </div>
        <div id="header-bar"></div>
        <div id="content">
            <h2>景点列表</h2>
            <table>
                <thead>
                <tr >
                    <th width="14%">ID</th>
                    <th width="20%">景点</th>
                    <th width="12%">印象图</th>
                    <th width="16%">旺季时间</th>
                    <th width="10%">旺季门票</th>
                    <th width="10%">淡季门票</th>
                    <th width="18%">操作</th>
                </tr>
                </thead>
                <tbody v-for="place in places">
                    <tr align="center">
                        <td rowspan="2"><span v-text="place.id"></span></td>
                        <td rowspan="2"><span v-text="place.name"></span></td>
                        <td><img :src="'data:image/jpg;base64,'+place.picPath" class="viewspotimg"></td>
                        <td><span v-text="place.hotTime"></span></td>
                        <td><span v-text="place.hotTicket"></span></td>
                        <td><span v-text="place.dimTicket"></span></td>
                        <td style="text-align: center">
                            <a href="javascript:;" @click="deletePlace(place.id)">删除景点</a><br>
                            <a :href="'updateviewspot.html?id='+place.id">修改景点</a>
                        </td>
                    </tr>
                    <tr>
                        <td colspan="5">
                            <div style="height: 56px;font-size: 14px;line-height: normal">
                                <b style="color:#467aa7">简介:</b> <span v-text="place.placeDes"></span>
                            </div>
                        </td>
                    </tr>
                </tbody>
            </table>
            <a :href="'./addviewspot.html?id='+id"><button type="button">添加景点</button></a>&emsp;
            <a href="../province/provincelist.html">返回省份列表</a>
            <div id="pages">
                <a href="javascript:;" v-if="page!=1" class="page" @click="findAllPage(page-1)">&lt;上一页</a>
                <a href="javascript:;" class="page" v-if="page==1">&lt;上一页</a>
                <span v-for="index in totalPage">
                    <a href="javascript:;" class="page" v-if="page==index"  v-text="index"></a>
                    <a href="javascript:;" class="page" v-if="page!=index"  v-text="index" @click="findAllPage(index)"></a>
                </span>
                <a href="javascript:;" v-if="page<totalPage" class="page" @click="findAllPage(page+1)">下一页&gt;</a>
                <a href="javascript:;" v-if="page==totalPage" class="page">下一页&gt;</a>
            </div>
        </div>
        <div id="footer">
            eternalreminisce@126.com
        </div>
    </div>
</div>

<script src="../js/vue.js"></script>
<script src="../js/axios.min.js"></script>
<script>
    var app = new Vue({
        el: "#app",
        data:{
            places:[],
            id:"",
            totalPage:0,
            page:1,
        },
        methods:{
            findAllPage(index){      // 封装成一个查询所有的函数
                _this = this;
                if (index) {
                    this.page = index;
                }
                axios.get("http://localhost:8080/place/findAllPage?provinceId="+this.id+'&page='+this.page).then((res)=>{
                    _this.places = res.data.places;
                    _this.totalPage = res.data.totalPage;
                    _this.page = res.data.page;
                })
            },
            deletePlace(id){        //删除景点信息
                _this = this;
                console.log(id);
                if (confirm('确定要删除景点吗?')) {
                    axios.get("http://localhost:8080/place/delete?id="+id).then((res)=>{
                        if (res.data.state) {
                            // 删除景点信息成功
                            alert(res.data.message+",点击确定刷新页面");
                            // 刷新当前页面
                            location.reload(true);
                        }else {
                            alert(res.data.message);
                        }
                    })
                }
            },
        },
        created(){
            let id = location.href.substring(location.href.indexOf("=")+1);
            this.id = id;
            console.log(id);
            this.findAllPage();
        }

    })
</script>
<script src="../js/date.js"></script>
</body>
</html>

(7)效果演示

在这里插入图片描述

四、项目源码下载地址

基于Spring Boot+Vue前后端分离旅游项目

写在最后:感谢您的阅读!如果觉得本篇博客写得不错,可以点赞+关注呦!

Logo

前往低代码交流专区

更多推荐