史上最全基于Spring Boot+Vue前后端分离旅游项目终极实战(附源码),没有之一!!!
文章目录一、需求分析二、库表设计三、项目编码1、环境搭建(1)、导入环境所需的依赖(2)、application.yml项目配置信息(3)、静态资源导入2、用户模块开发(1)、用户注册+用户登录 后端处理①在entity包下新建User实体类②在utils包下新建图片验证码类③在dao包下新建UserDao类④在mapper目录下新建userDao.xml⑤在service包下新建UserServ
写在前面:欢迎各位博主前来阅读我的博客,希望通过本篇博客你可以有所收获。如有任何问题,欢迎小伙伴们在评论区评论。由于本人水平有限,如有不足,请您指正,谢谢!
想必小伙们通过博客的标题也看到了,这是一个关于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">账 号:</div>
<input type="text" v-model="user.username" name="username">
</label>
<label>
<div class="label-text">密 码:</div>
<input type="password" v-model="user.password" name="password">
</label>
<label>
<div class="label-text">邮 箱:</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> 
<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">账 号:</div>
<input type="text" name="username" v-model="user.username">
</label>
<label>
<div class="label-text">密 码:</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> 
<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">省 份:</div>
<input type="text" v-model="province.name">
</label>
<label>
<div class="label-text" >标 签:</div>
<input type="text" v-model="province.tags">
</label>
<button type="button" @click="saveProvinceInfo">提 交</button> 
<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"><上一页</a>
<a href="javascript:;" class="page" @click="findAll(page-1)" v-if="page==1" disabled=""><上一页</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">下一页></a>
<a href="javascript:;" class="page" v-if="page!=totalPage" @click="findAll(page+1)">下一页></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">省 份:</div>
<input type="text" v-model="province.name">
</label>
<label>
<div class="label-text">标 签:</div>
<input type="text" v-model="province.tags">
</label>
<button type="button" @click="updateProvince">提 交</button> 
<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">景  点:</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> 
<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">景  点:</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> 
<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> 
<a href="../province/provincelist.html">返回省份列表</a>
<div id="pages">
<a href="javascript:;" v-if="page!=1" class="page" @click="findAllPage(page-1)"><上一页</a>
<a href="javascript:;" class="page" v-if="page==1"><上一页</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)">下一页></a>
<a href="javascript:;" v-if="page==totalPage" class="page">下一页></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)效果演示
四、项目源码下载地址
写在最后:感谢您的阅读!如果觉得本篇博客写得不错,可以点赞+关注呦!
更多推荐
所有评论(0)