项目名称:旅游网站后台管理
一:项目简介
旅游网站后台管理,包括如下
用户:
旅游线路:
线路图片:
线路分类:
旅行社:
后台技术:
springboot、mybatis、mybatis plus
前台:
bootstrap、jquery、thymeleaf、fileinput插件
数据库:
mysql
数据库设计:
表结构:
二:创建项目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lxs.travel</groupId>
<artifactId>travel-springboot-mp</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- 依赖spring-boot-starter-parent构件时,从本地仓库-> 远程仓库获取,不从本地目录获取 -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--mybatis plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!--fileupload-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.0.RELEASE</version>
</plugin>
</plugins>
</build>
</project>
application.yml:
server:
port: 80
servlet:
context-path: / #项目的上下文路径
spring:
datasource:
url: jdbc:mysql://localhost:3306/travel
username: root
password: linda198721
driver-class-name: com.mysql.jdbc.Driver
hikari:
idle-timeout: 60000
maximum-pool-size: 30
minimum-idle: 10
thymeleaf:
cache: false
#mybatis plus配置
mybatis-plus:
mapper-locations: classpath:/mybatis/*.xml #加载映射文件
type-aliases-package: com.lxs.travel.domain #别名搜索包
configuration:
lazy-loading-enabled: true #打开懒加载
aggressive-lazy-loading: false #关闭积极加载
启动器:
package com.lxs.travel;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TravelApplication {
public static void main(String[] args) {
SpringApplication.run(TravelApplication.class, args);
}
}
整合mybatis plus:
package com.lxs.travel.config;
import com.github.pagehelper.PageInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.lxs.travel.dao")
public class MybatisPlusConfig {
@Bean
public PageInterceptor pageInterceptor(){
return new PageInterceptor();
}
}
三:用户管理
实体类:
Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率
使用lomhok简化实体类开发,具体lomhok语法和安装参考资料lomhok入门.html文档
package com.lxs.travel.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("tab_user")
public class User implements Serializable {
@TableId(type = IdType.AUTO)
private Integer uid;
private String username;
private String password;
private String name;
private Date birthday;
private String sex;
private String telephone;
private String email;
private String status;
private String code;
private Boolean isadmin;
}
dao:
package com.lxs.travel.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lxs.travel.domain.User;
public interface UserDao extends BaseMapper<User> {
}
注意:这里使用mybatis plus内置方法,没有映射文件
service:
package com.lxs.travel.service;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.domain.User;
import java.util.List;
public interface UserService {
/**
* 分页查询
* @param conditioin 查询条件
* @return
*/
public PageInfo<User> findPage(User conditioin, int pageNum, int pageSize);
/**
* 查询
* @param conditioin 查询条件
* @return
*/
public List<User> find(User conditioin);
/**
* 添加
* @param user
* @return
*/
public int add(User user);
/**
* 根据ID查询用户
* @param id
* @return
*/
public User findById(Integer id);
/**
* 修改
* @param user
* @return
*/
public int update(User user);
/**
* 删除
* * @param id
* * @return
* */
public int delete(Integer id);
}
实现类:
package com.lxs.travel.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.dao.UserDao;
import com.lxs.travel.domain.User;
import com.lxs.travel.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public PageInfo<User> findPage(User conditioin, int pageNum, int pageSize) {
return PageHelper.startPage(pageNum,pageSize).doSelectPageInfo(()->{
userDao.selectList(Wrappers.<User>query());
});
}
@Override
public List<User> find(User conditioin) {
return userDao.selectList(Wrappers.<User>query());
}
@Override
public int add(User user) {
return userDao.insert(user);
}
@Override
public User findById(Integer id) {
return userDao.selectById(id);
}
@Override
public int update(User user) {
return userDao.updateById(user);
}
@Override
public int delete(Integer id) {
return userDao.deleteById(id);
}
}
注意:分页方法采用的lambda表达式的写法
测试:
package com.lxs.travel.service.impl;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.domain.User;
import com.lxs.travel.service.UserService;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest extends TestCase {
@Autowired
private UserService userService;
@Test
public void testFindPage() {
User u = new User();
PageInfo<User> page = userService.findPage(u, 1, 10);
page.getList().forEach(System.out :: println);
System.out.println("总行数=" + page.getTotal());
System.out.println("当前页=" + page.getPageNum());
System.out.println("每页行数=" + page.getPageSize());
System.out.println("总页数=" + page.getPages());
System.out.println("起始行数=" + page.getStartRow());
System.out.println("是第一页=" + page.isIsFirstPage());
System.out.println("是最后页=" + page.isIsLastPage());
System.out.println("还有下一页=" + page.isHasNextPage());
System.out.println("还有上一页=" + page.isHasPreviousPage());
System.out.println("页码列表" + Arrays.toString(page.getNavigatepageNums()));
}
}
controller:
package com.lxs.travel.controller;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.domain.User;
import com.lxs.travel.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/admin/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 分页查询
*/
@RequestMapping("/page")
public String page(User user,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
Model model){
PageInfo<User> page = userService.findPage(user, pageNum, pageSize);
model.addAttribute("page",page);
return "/user/list";
}
/**
* 跳转到加载页面
*/
@RequestMapping("/toAdd")
public String toAdd(){
return "/user/add";
}
/**
* 添加
*/
@RequestMapping("/doAdd")
public String doAdd(User user){
userService.add(user);
return "redirect:/admin/user/page";
}
/**
* 跳转到修改页面
*/
@RequestMapping("/toupdate/{id}")
public String toUpdate(@PathVariable("id") Integer id,Model model){
User user = userService.findById(id);
model.addAttribute("user",user);
return "/user/update";
}
/**
* 修改
*/
@RequestMapping("/doupdate")
public String doUpdate(User user){
userService.update(user);
return "redirect:/admin/user/page";
}
/**
* 删除
*/
@RequestMapping("/delete/{id}")
public String delete(@PathVariable("id") Integer id){
userService.delete(id);
return "redirect:/admin/user/page";
}
/**
* 批量删除
*/
@RequestMapping("/delete")
public String batchDelete(@RequestParam("ids") Integer[] ids){
for (Integer id : ids) {
userService.delete(id);
}
return "redirect:/admin/user/page";
}
}
页面:
前端技术栈 bootstrap,jquery,ace, fileinput插件
这里我们直接拷贝index.html,header.html,和left.html
list.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:javascript="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta charset="utf-8"/>
<title>用户管理</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<!-- bootstrap & fontawesome -->
<link rel="stylesheet" href="/assets/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/assets/font-awesome/4.5.0/css/font-awesome.min.css"/>
<!-- page specific plugin styles -->
<!-- text fonts -->
<link rel="stylesheet" href="/assets/css/fonts.googleapis.com.css"/>
<!-- ace styles -->
<link rel="stylesheet" href="/assets/css/ace.min.css" class="ace-main-stylesheet" id="main-ace-style"/>
<!--[if lte IE 9]>
<link rel="stylesheet" href="/assets/css/ace-part2.min.css" class="ace-main-stylesheet"/>
<![endif]-->
<link rel="stylesheet" href="/assets/css/ace-skins.min.css"/>
<link rel="stylesheet" href="/assets/css/ace-rtl.min.css"/>
<!--[if lte IE 9]>
<link rel="stylesheet" href="/assets/css/ace-ie.min.css"/>
<![endif]-->
<!-- inline styles related to this page -->
<!-- ace settings handler -->
<script src="/assets/js/ace-extra.min.js"></script>
<!-- HTML5shiv and Respond.js for IE8 to support HTML5 elements and media queries -->
<!--[if lte IE 8]>
<script src="/assets/js/html5shiv.min.js"></script>
<script src="/assets/js/respond.min.js"></script>
<![endif]-->
<!--[if !IE]> -->
<script src="/assets/js/jquery-2.1.4.min.js"></script>
<!-- <![endif]-->
<!--[if IE]>
<script src="/assets/js/jquery-1.11.3.min.js"></script>
<![endif]-->
<script src="/assets/js/bootstrap.min.js"></script>
<!-- page specific plugin scripts -->
<script src="/assets/js/jquery.dataTables.min.js"></script>
<script src="/assets/js/jquery.dataTables.bootstrap.min.js"></script>
<script src="/assets/js/dataTables.buttons.min.js"></script>
<script src="/assets/js/buttons.flash.min.js"></script>
<script src="/assets/js/buttons.html5.min.js"></script>
<script src="/assets/js/buttons.print.min.js"></script>
<script src="/assets/js/buttons.colVis.min.js"></script>
<script src="/assets/js/dataTables.select.min.js"></script>
<!-- ace scripts -->
<script src="/assets/js/ace-elements.min.js"></script>
<script src="/assets/js/ace.min.js"></script>
<!-- TODO 页面删除js -->
<script>
$(function (){
$("#checkAll").change(function (){
$(":checkbox[name='ids']").prop("checked",$(this).prop("checked"));
});
$("#btnDelete").click(function (){
if($(":checked[name='ids']").length > 0){
$("#df").submit();
}else {
alert("请选择要删除的记录");
}
})
})
</script>
</head>
<body class="no-skin">
<div th:replace="header :: navbar"></div>
<div class="main-container ace-save-state" id="main-container">
<script type="text/javascript">
try {
ace.settings.loadState('main-container')
} catch (e) {
}
</script>
<div th:replace="left :: sidebar"></div>
<div class="main-content">
<div class="main-content-inner">
<div class="breadcrumbs ace-save-state" id="breadcrumbs">
<ul class="breadcrumb">
<li>
<i class="ace-icon fa fa-home home-icon"></i>
<a href="#">首页</a>
</li>
<li>
<a href="#">用户</a>
</li>
<li class="active">用户管理</li>
</ul><!-- /.breadcrumb -->
</div>
<div class="page-content">
<div style="float: left; padding: 10px;">
<form class="form-inline" id="qf" action="/admin/user/page" method="post">
<input type="hidden" name="pageNum" id="pageNum" th:value="${page.pageNum}">
<input type="hidden" name="pageSize" id="pageSize" th:value="${page.pageSize}">
<div class="form-group">
<label for="name">姓名</label>
<input type="text" name="name" class="form-control" id="name" th:value="${param.name}">
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input type="text" name="email" class="form-control" id="email" th:value="${param.email}">
</div>
<button type="submit" class="btn btn-sm btn-default">查询</button>
<button type="button" class="btn btn-sm btn-default" onclick="location.href='/admin/user/toAdd'">添加</button>
<button type="button" class="btn btn-sm btn-default" id="btnDelete">删除</button>
</form>
</div>
<!--TODO 删除表单-->
<form action="/admin/user/delete" method="post" id="df">
<table id="simple-table" class="table table-bordered table-hover">
<thead>
<tr>
<th><input type="checkbox" id="checkAll"></th>
<th>姓名</th>
<th>性别</th>
<th>出生日期</th>
<th>电话</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<!-- TODO 回显数据 -->
</thead>
<tbody>
<tr th:each="u, status : ${page.list}" th:style="${status.even} ?'background-color: grey'">
<td><input type="checkbox" name="ids" th:value="${u.uid}"></td>
<td th:text="${u.name}"></td>
<td th:text="${u.sex}"></td>
<td th:text="${#dates.format(u.birthday, 'yyyy-MM-dd')}"></td>
<td th:text="${u.telephone}"></td>
<td th:text="${u.email}"></td>
<td>
<div class="hidden-sm hidden-xs btn-group">
<a class="btn btn-xs btn-info"
th:href="|/admin/user/toupdate/${u.uid}|" >
<i class="ace-icon fa fa-pencil bigger-120"></i>
</a>
<a class="btn btn-xs btn-info"
th:href="|/admin/user/delete/${u.uid}|">
<i class="ace-icon fa fa-trash-o bigger-120"></i>
</a>
</div>
</td>
</tr>
</tbody>
</table>
</form>
<!-- TODO 回显数据 -->
<div>
<nav aria-label="Page navigation">
<ul class="pagination">
<li id="first">
<a href="javascript:void(0);">
<span aria-hidden="true">首页</span>
</a>
</li>
<li id="prev">
<a href="javascript:void(0);" aria-label="Previous">
<span aria-hidden="true">上一页</span>
</a>
</li>
<li name="pageNum" th:each="i : ${page.navigatepageNums}" th:class="${i==page.pageNum} ? 'active'">
<a href="javascript:void(0);" th:text="${i}" ></a>
</li>
<li id="next">
<a href="javascript:void(0);" aria-label="Next">
<span aria-hidden="true">下一页</span>
</a>
</li>
<li id="last">
<a href="javascript:void(0);">
<span aria-hidden="true">末页</span>
</a>
</li>
<span style="font-size: 20px;margin-left: 5px;"
th:text="|共条${page.total} 记录,共${page.pages}页, 每页${page.pageSize}行数|">
</span>
<select id="setRows">
<option value="5" >5</option>
<option value="10" >10</option>
<option value="20" >20</option>
<option value="30" >30</option>
</select>
</ul>
</nav>
</div>
<!-- TODO 分页JS -->
<script th:inline="javascript">
//得到初始化变量
var pageNum=[[${page.pageNum}]]; //当前页
var pages=[[${page.pages}]]; //总页数
var hasNextPage=[[${page.hasNextPage}]]; //true 还有下一页
var hasPrevious=[[${page.hasPreviousPage}]]; //还有上一页
//判断按钮状态
if(!hasPrevious){
$("#prev").addClass("disabled");
$("#first").addClass("disabled");
}
if(!hasNextPage){
$("#next").addClass("disabled");
$("#last").addClass("disabled");
}
//设置按钮的监听事件
$("#first").click(function (){
if(!$("#first").hasClass("disabled")){
$("#pageNum").val(1);
$("#qf").submit();
}
});
$("#prev").click(function (){
if(!$("#prev").hasClass("disabled")){
$("#pageNum").val(pageNum-1);
$("#qf").submit();
}
})
$("#next").click(function (){
if(!$("#next").hasClass("disabled")){
$("#pageNum").val(pageNum+1);
$("#qf").submit();
}
})
$("#last").click(function (){
if(!$("#last").hasClass("disabled")){
$("#pageNum").val(pages);
$("#qf").submit();
}
})
//页码分页
$("li[name='pageNum']").click(function (){
if(!$(this).hasClass("active")){
$("#pageNum").val($(this).children("a").html());
$("#qf").submit();
}
});
//设置每页行数
$("#setRows").change(function (){
$("#pageSize").val($(this).val());
$("#pageNum").val(1);
$("#qf").submit();
});
</script>
</div><!-- /.page-content -->
</div>
</div><!-- /.main-content -->
</div><!-- /.main-container -->
</body>
</html>
add.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta charset="utf-8" />
<title>用户管理</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<!-- bootstrap & fontawesome -->
<link rel="stylesheet" href="/assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="/assets/font-awesome/4.5.0/css/font-awesome.min.css" />
<!-- page specific plugin styles -->
<!-- text fonts -->
<link rel="stylesheet" href="/assets/css/fonts.googleapis.com.css" />
<!-- ace styles -->
<link rel="stylesheet" href="/assets/css/ace.min.css" class="ace-main-stylesheet" id="main-ace-style" />
<!--[if lte IE 9]>
<link rel="stylesheet" href="/assets/css/ace-part2.min.css" class="ace-main-stylesheet" />
<![endif]-->
<link rel="stylesheet" href="/assets/css/ace-skins.min.css" />
<link rel="stylesheet" href="/assets/css/ace-rtl.min.css" />
<!--[if lte IE 9]>
<link rel="stylesheet" href="/assets/css/ace-ie.min.css" />
<![endif]-->
<!-- inline styles related to this page -->
<!-- ace settings handler -->
<script src="/assets/js/ace-extra.min.js"></script>
<!-- HTML5shiv and Respond.js for IE8 to support HTML5 elements and media queries -->
<!--[if lte IE 8]>
<script src="/assets/js/html5shiv.min.js"></script>
<script src="/assets/js/respond.min.js"></script>
<![endif]-->
<!--[if !IE]> -->
<script src="/assets/js/jquery-2.1.4.min.js"></script>
<!-- <![endif]-->
<!--[if IE]>
<script src="/assets/js/jquery-1.11.3.min.js"></script>
<![endif]-->
<script src="/assets/js/bootstrap.min.js"></script>
<!-- page specific plugin scripts -->
<script src="/assets/js/jquery.dataTables.min.js"></script>
<script src="/assets/js/jquery.dataTables.bootstrap.min.js"></script>
<script src="/assets/js/dataTables.buttons.min.js"></script>
<script src="/assets/js/buttons.flash.min.js"></script>
<script src="/assets/js/buttons.html5.min.js"></script>
<script src="/assets/js/buttons.print.min.js"></script>
<script src="/assets/js/buttons.colVis.min.js"></script>
<script src="/assets/js/dataTables.select.min.js"></script>
<!-- ace scripts -->
<script src="/assets/js/ace-elements.min.js"></script>
<script src="/assets/js/ace.min.js"></script>
<script language="javascript" type="text/javascript" src="/js/My97DatePicker/WdatePicker.js"></script>
</head>
<body class="no-skin">
<div th:replace="header :: navbar"></div>
<div class="main-container ace-save-state" id="main-container">
<script type="text/javascript">
try{ace.settings.loadState('main-container')}catch(e){}
</script>
<div th:replace="left :: sidebar"></div>
<div class="main-content">
<div class="main-content-inner">
<div class="breadcrumbs ace-save-state" id="breadcrumbs">
<ul class="breadcrumb">
<li>
<i class="ace-icon fa fa-home home-icon"></i>
<a href="#">首页</a>
</li>
<li>
<a href="#">用户</a>
</li>
<li class="active">用户管理</li>
</ul><!-- /.breadcrumb -->
</div>
<div class="page-content">
<form action="/admin/user/doAdd" method="post">
<div class="form-group">
<label for="name">姓名:</label>
<input type="text" class="form-control" id="name" name="name" placeholder="请输入姓名">
</div>
<div class="form-group">
<label>性别:</label>
<input type="radio" name="sex" value="男" checked="checked"/>男
<input type="radio" name="sex" value="女"/>女
</div>
<div class="form-group">
<label for="birthday">生日:</label>
<input type="text" class="form-control" id="birthday" name="birthday" placeholder="请输入生日" onClick="WdatePicker({el:this,dateFmt:'yyyy-MM-dd'})">
</div>
<div class="form-group">
<label for="telephone">电话:</label>
<input type="text" class="form-control" name="telephone" id="telephone" placeholder="请输入电话"/>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="text" class="form-control" name="email" id="email" placeholder="请输入邮箱地址"/>
</div>
<div class="form-group">
<label for="username">登录名</label>
<input type="text" class="form-control" name="username" id="username" placeholder="请输入登录名"/>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="text" class="form-control" name="password" id="password" placeholder="请输入密码"/>
</div>
<div class="form-group" style="text-align: center">
<input class="btn btn-primary" type="submit" value="提交" />
<input class="btn btn-default" type="reset" value="重置" />
<input class="btn btn-default" type="button" value="返回" />
</div>
</form>
</div><!-- /.page-content -->
</div>
</div><!-- /.main-content -->
</div><!-- /.main-container -->
</body>
</html>
update.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta charset="utf-8" />
<title>用户管理</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<!-- bootstrap & fontawesome -->
<link rel="stylesheet" href="/assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="/assets/font-awesome/4.5.0/css/font-awesome.min.css" />
<!-- page specific plugin styles -->
<!-- text fonts -->
<link rel="stylesheet" href="/assets/css/fonts.googleapis.com.css" />
<!-- ace styles -->
<link rel="stylesheet" href="/assets/css/ace.min.css" class="ace-main-stylesheet" id="main-ace-style" />
<!--[if lte IE 9]>
<link rel="stylesheet" href="/assets/css/ace-part2.min.css" class="ace-main-stylesheet" />
<![endif]-->
<link rel="stylesheet" href="/assets/css/ace-skins.min.css" />
<link rel="stylesheet" href="/assets/css/ace-rtl.min.css" />
<!--[if lte IE 9]>
<link rel="stylesheet" href="/assets/css/ace-ie.min.css" />
<![endif]-->
<!-- inline styles related to this page -->
<!-- ace settings handler -->
<script src="/assets/js/ace-extra.min.js"></script>
<!-- HTML5shiv and Respond.js for IE8 to support HTML5 elements and media queries -->
<!--[if lte IE 8]>
<script src="/assets/js/html5shiv.min.js"></script>
<script src="/assets/js/respond.min.js"></script>
<![endif]-->
<!--[if !IE]> -->
<script src="/assets/js/jquery-2.1.4.min.js"></script>
<!-- <![endif]-->
<!--[if IE]>
<script src="/assets/js/jquery-1.11.3.min.js"></script>
<![endif]-->
<script src="/assets/js/bootstrap.min.js"></script>
<!-- page specific plugin scripts -->
<script src="/assets/js/jquery.dataTables.min.js"></script>
<script src="/assets/js/jquery.dataTables.bootstrap.min.js"></script>
<script src="/assets/js/dataTables.buttons.min.js"></script>
<script src="/assets/js/buttons.flash.min.js"></script>
<script src="/assets/js/buttons.html5.min.js"></script>
<script src="/assets/js/buttons.print.min.js"></script>
<script src="/assets/js/buttons.colVis.min.js"></script>
<script src="/assets/js/dataTables.select.min.js"></script>
<!-- ace scripts -->
<script src="/assets/js/ace-elements.min.js"></script>
<script src="/assets/js/ace.min.js"></script>
<script language="javascript" type="text/javascript" src="/js/My97DatePicker/WdatePicker.js"></script>
</head>
<body class="no-skin">
<div th:replace="header :: navbar"></div>
<div class="main-container ace-save-state" id="main-container">
<script type="text/javascript">
try{ace.settings.loadState('main-container')}catch(e){}
</script>
<div th:replace="left :: sidebar"></div>
<div class="main-content">
<div class="main-content-inner">
<div class="breadcrumbs ace-save-state" id="breadcrumbs">
<ul class="breadcrumb">
<li>
<i class="ace-icon fa fa-home home-icon"></i>
<a href="#">首页</a>
</li>
<li>
<a href="#">用户</a>
</li>
<li class="active">用户管理</li>
</ul><!-- /.breadcrumb -->
</div>
<div class="page-content">
<!-- TODO 回显数据 -->
<form action="/admin/user/doupdate" method="post" th:object="${user}" >
<input type="hidden" name="uid" >
<div class="form-group">
<label for="name">姓名:</label>
<!-- TODO 回显姓名 -->
<input type="text" class="form-control" id="name" name="name" placeholder="请输入姓名" th:value="*{name}" >
</div>
<div class="form-group">
<label>性别:</label>
<input type="radio" name="sex" value="男" th:checked="'男' == *{sex}" />男
<input type="radio" name="sex" value="女" th:checked="'女' == *{sex}" />女
</div>
<div class="form-group">
<label for="birthday">生日:</label>
<input type="text" class="form-control" id="birthday" name="birthday" placeholder="请输入生日" onClick="WdatePicker({el:this,dateFmt:'yyyy-MM-dd'})" th:value="*{birthday}">
</div>
<div class="form-group">
<label for="telephone">电话:</label>
<input type="text" class="form-control" name="telephone" id="telephone" placeholder="请输入电话" th:value="*{telephone}"/>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="text" class="form-control" name="email" id="email" placeholder="请输入邮箱地址" th:value="*{email}"/>
</div>
<div class="form-group">
<label for="username">登录名</label>
<input type="text" class="form-control" name="username" id="username" placeholder="请输入登录名" th:value="*{username}"/>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="text" class="form-control" name="password" id="password" placeholder="请输入密码" th:value="*{password}"/>
</div>
<div class="form-group" style="text-align: center">
<input class="btn btn-primary" type="submit" value="提交" />
<input class="btn btn-default" type="reset" value="重置" />
<input class="btn btn-default" type="button" value="返回" />
</div>
</form>
</div><!-- /.page-content -->
</div>
</div><!-- /.main-content -->
</div><!-- /.main-container -->
</body>
</html>
多选删除:
/**
* 批量删除
*/
@RequestMapping("/delete")
public String batchDelete(@RequestParam("ids") Integer[] ids){
for (Integer id : ids) {
userService.delete(id);
}
return "redirect:/admin/user/page";
}
页面
<!-- TODO 页面删除js -->
<script>
$(function (){
$("#checkAll").change(function (){
$(":checkbox[name='ids']").prop("checked",$(this).prop("checked"));
});
$("#btnDelete").click(function (){
if($(":checked[name='ids']").length > 0){
$("#df").submit();
}else {
alert("请选择要删除的记录");
}
})
})
</script>
注入在springmvc中配置日期转换器
package com.lxs.travel.utils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Component
public class DateConverterConfig implements Converter<String, Date> {
private static final List<String> formarts = new ArrayList<>(4);
static{
formarts.add("yyyy-MM");
formarts.add("yyyy-MM-dd");
formarts.add("yyyy-MM-dd hh:mm");
formarts.add("yyyy-MM-dd hh:mm:ss");
}
@Override
public Date convert(String source) {
String value = source.trim();
if ("".equals(value)) {
return null;
}
if(source.matches("^\\d{4}-\\d{1,2}$")){
return parseDate(source, formarts.get(0));
}else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")){
return parseDate(source, formarts.get(1));
}else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")){
return parseDate(source, formarts.get(2));
}else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")){
return parseDate(source, formarts.get(3));
}else {
throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
}
}
/**
* 格式化日期
* @param dateStr String 字符型日期
* @param format String 格式
* @return Date 日期
*/
public Date parseDate(String dateStr, String format) {
Date date=null;
try {
DateFormat dateFormat = new SimpleDateFormat(format);
date = dateFormat.parse(dateStr);
} catch (Exception e) {
}
return date;
}
}
四:旅游公司
参考用户管理CRUD操作,具体代码和用户管理相似
实体类
package com.lxs.travel.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("tab_seller")
public class Seller implements Serializable {
@TableId(type = IdType.AUTO)
private Integer sid;//商家id
private String sname;//商家名称
private String consphone;//商家电话
private String address;//商家地址
}
dao
package com.lxs.travel.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lxs.travel.domain.Seller;
import org.apache.ibatis.annotations.Select;
public interface SellerDao extends BaseMapper<Seller> {
@Select("SELECT * FROM tab_seller WHERE sid=#{id}")
public Seller findById(Integer id);
}
service
接口
package com.lxs.travel.service;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.domain.Seller;
import java.util.List;
public interface SellerService {
/**
* 分页查询
* @param conditioin 查询条件
* @return
*/
public PageInfo<Seller> findPage(Seller conditioin, int pageNum, int pageSize);
/**
* 查询
* @param conditioin 查询条件
* @return
*/
public List<Seller> find(Seller conditioin);
/**
* 添加
* @param seller
* @return
*/
public int add(Seller seller);
/**
* 根据ID查询用户
* @param id
* @return
*/
public Seller findById(Integer id);
/**
* 修改
* @param seller
* @return
*/
public int update(Seller seller);
/**
* 删除
* @param id
* @return
*/
public int delete(Integer id);
}
实现类
package com.lxs.travel.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.dao.SellerDao;
import com.lxs.travel.domain.Seller;
import com.lxs.travel.service.SellerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SellerServiceImpl implements SellerService {
@Autowired
private SellerDao sellerDao;
@Override
public PageInfo<Seller> findPage(Seller conditioin, int pageNum, int pageSize) {
return PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(() -> {
sellerDao.selectList(new QueryWrapper<Seller>());
});
}
@Override
public List<Seller> find(Seller condition) {
return sellerDao.selectList(Wrappers.query());
}
@Override
public int add(Seller seller) {
return sellerDao.insert(seller);
}
@Override
public Seller findById(Integer id) {
return sellerDao.selectById(id);
}
@Override
public int update(Seller seller) {
return sellerDao.updateById(seller);
}
@Override
public int delete(Integer id) {
return sellerDao.deleteById(id);
}
}
controller
package com.lxs.travel.controller;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.domain.Seller;
import com.lxs.travel.service.SellerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/admin/seller")
public class SellerController {
@Autowired
private SellerService sellerService;
@RequestMapping("/page")
public String page(
Seller seller,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
Model model) {
PageInfo<Seller> page = sellerService.findPage(seller, pageNum, pageSize);
model.addAttribute("page", page);
return "/seller/list";
}
@RequestMapping("/toadd")
public String toAdd() {
return "seller/add";
}
@RequestMapping("/doadd")
public String doAdd(Seller seller) {
sellerService.add(seller);
return "redirect:/admin/seller/page";
}
@RequestMapping("/toupdate")
public String toUpdate(Integer id, Model model) {
Seller seller = sellerService.findById(id);
model.addAttribute("seller", seller);
return "/seller/update";
}
@RequestMapping("/doupdate")
public String doUpdate(Seller seller) {
sellerService.update(seller);
return "redirect:/admin/seller/page";
}
@RequestMapping("/delete")
public String delete(Integer id) {
sellerService.delete(id);
return "redirect:/admin/seller/page";
}
}
页面
页面代码和用户crud基本一致,直接从素材拷贝即可
五:线路分类
实体类
package com.lxs.travel.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("tab_category")
public class Category implements Serializable {
@TableId(type = IdType.AUTO)
private Integer cid;//分类id
private String cname;//分类名称
}
dao
package com.lxs.travel.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lxs.travel.domain.Category;
import org.apache.ibatis.annotations.Select;
public interface CategoryDao extends BaseMapper<Category> {
@Select("SELECT cname,cid FROM tab_category WHERE cid=#{id}")
public Category findById(Integer id);
}
service
接口
package com.lxs.travel.service;
import com.lxs.travel.domain.Category;
import java.util.List;
public interface CategoryService {
public List<Category> find();
/**
* 添加
* @param category
* @return
*/
public int add(Category category);
/**
* 根据ID查询
* @param id
* @return
*/
public Category findById(Integer id);
/**
* 修改
* @param category
* @return
*/
public int update(Category category);
/**
* 删除
* @param id
* @return
*/
public int delete(Integer id);
}
实现类
package com.lxs.travel.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lxs.travel.dao.CategoryDao;
import com.lxs.travel.domain.Category;
import com.lxs.travel.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryDao categoryDao;
@Override
public List<Category> find() {
return categoryDao.selectList(Wrappers.query());
}
@Override
public int add(Category category) {
return categoryDao.insert(category);
}
@Override
public Category findById(Integer id) {
return categoryDao.selectById(id);
}
@Override
public int update(Category category) {
return categoryDao.updateById(category);
}
@Override
public int delete(Integer id) {
return categoryDao.deleteById(id);
}
}
controller
package com.lxs.travel.controller;
import com.lxs.travel.domain.Category;
import com.lxs.travel.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/admin/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@RequestMapping("/list")
public String find(Model model) {
List<Category> list = categoryService.find();
model.addAttribute("list", list);
return "/category/list";
}
@RequestMapping("/toadd")
public String toAdd() {
return "/category/add";
}
@RequestMapping("/doadd")
public String doAdd(Category category) {
categoryService.add(category);
return "redirect:/admin/category/list";
}
@RequestMapping("/toupdate")
public String toUpdate(Integer id, Model model) {
Category category = categoryService.findById(id);
model.addAttribute("category", category);
return "/category/update";
}
@RequestMapping("/doupdate")
public String doUpdate(Category category) {
categoryService.update(category);
return "redirect:/admin/category/list";
}
@RequestMapping("delete")
public String delete(Integer id) {
categoryService.delete(id);
return "redirect:/admin/category/list";
}
}
页面
页面代码直接从素材拷贝即可,注意分类因为数据较少,可以不用分页
六:旅游线路(重点)
需求和表结构
旅游线路主表(线路名称,详情,介绍,价格,缩略图)
旅游线详细图片表(小图,大图)
实体类
线路实体类
package com.lxs.travel.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
@TableName("tab_route")
public class Route implements Serializable {
@TableId(type = IdType.AUTO)
private Integer rid;//线路id,必输
private String rname;//线路名称,必输
private Double price;//价格,必输
private String routeIntroduce;//线路介绍
private String rflag; //是否上架,必输,0代表没有上架,1代表是上架
private String rdate; //上架时间
private String isThemeTour;//是否主题旅游,必输,0代表不是,1代表是
private Integer count;//收藏数量
private Integer cid;//所属分类,必输
private String rimage;//缩略图
private Integer sid;//所属商家
private String sourceId;//抓取数据的来源id
@TableField(exist = false)
private Category category;//所属分类 使用resultmap的assocation处理
@TableField(exist = false)
private Seller seller;//所属商家 使用resultmap的assocation处理
@TableField(exist = false)
private List<RouteImg> routeImgList;//商品详情图片列表,关联属性,mybatis plus不能查,需要配置resultmap使用resultmap的collection处理
}
线路的图片列表
package com.lxs.travel.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("tab_route_img")
public class RouteImg implements Serializable {
@TableId(type = IdType.AUTO)
private int rgid;//商品图片id
private int rid;//旅游商品id
private String bigpic;//详情商品大图
private String smallpic;//详情商品小图
}
dao
线路dao
package com.lxs.travel.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lxs.travel.domain.Route;
import java.util.List;
public interface RouteDao extends BaseMapper<Route> {
/**
* 分页查询
* @param conditioin
* @return
*/
public List<Route> find(Route conditioin);
/**
* 根据ID查询用户
* @param id
* @return
*/
public Route findById(Integer id);
}
映射文件
因为线路需要查询线路详细图片列表 routeImgList ,线路所属分类 category ,线路所属旅行社 seller ,这里在xml映射文件中使用resultMap的association和collection查询,不能使用mybatis plus内置查询
<?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.lxs.travel.dao.RouteDao">
<sql id="selectSql">
SELECT
*
FROM
tab_route
</sql>
<select id="find" resultMap="routeMap">
<include refid="selectSql"></include>
<where>
<if test="cid !=null">
cid = #{cid}
</if>
<if test="sid !=null">
and sid = #{sid}
</if>
<if test="rname !=null and rname.trim() != ''">
and rname like '%${rname}%'
</if>
<if test="routeIntroduce != null and routeIntroduce.trim() != ''">
and routeIntroduce like '%${routeIntroduce}%'
</if>
</where>
</select>
<resultMap id="routeMap" type="route">
<id column="rid" property="rid"></id>
<association property="category" javaType="category"
select="com.lxs.travel.dao.CategoryDao.findById" column="cid">
<id column="cid" property="cid"></id>
</association>
<association property="seller" javaType="seller"
select="com.lxs.travel.dao.SellerDao.findById" column="sid">
<id column="sid" property="sid"></id>
</association>
<collection property="routeImgList" javaType="java.util.List" ofType="RouteImg"
select="com.lxs.travel.dao.RouteImgDao.findByRid" column="rid">
<id column="rgid" property="rgid"></id>
</collection>
</resultMap>
<select id="findById" parameterType="int" resultMap="routeMap">
<include refid="selectSql"></include>
where
rid = #{rid}
</select>
</mapper>
注意:这里因为要查询旅游线路的所属分类( select="cn.lxs.travel.dao.CategoryDao.findById" ),所属旅行社( select="cn.lxs.travel.dao.SellerDao.findById" ),线路详细图片列表
( select="cn.lxs.travel.dao.RouteImgDao.findByRid" )
所以需要分在在CategoryDao中增加findById方法如下:
package com.lxs.travel.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lxs.travel.domain.Category;
import org.apache.ibatis.annotations.Select;
public interface CategoryDao extends BaseMapper<Category> {
@Select("SELECT cname,cid FROM tab_category WHERE cid=#{id}")
public Category findById(Integer id);
}
SellerDao增加findById方法如下:
package com.lxs.travel.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lxs.travel.domain.Seller;
import org.apache.ibatis.annotations.Select;
public interface SellerDao extends BaseMapper<Seller> {
@Select("SELECT * FROM tab_seller WHERE sid=#{id}")
public Seller findById(Integer id);
}
RouteImgDao增加findByRid方法:如下
package com.lxs.travel.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lxs.travel.domain.RouteImg;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface RouteImgDao extends BaseMapper<RouteImg> {
@Select("select * from tab_route_img where rid = #{rid}")
public List<RouteImg> findByRid(Integer rid);
}
service
线路service:接口
package com.lxs.travel.service;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.domain.Route;
public interface RouteService {
/**
* 分页查询
* @param conditioin 查询条件
* @return
*/
public PageInfo<Route> findPage(Route conditioin, int pageNum, int pageSize);
/**
* 添加
* @param route
* @return
*/
public int add(Route route);/**
* 根据ID查询用户
* @param id
* @return
*/
public Route findById(Integer id);
/**
* 修改
* @param route
* @return
*/
public int update(Route route);
/**
* 删除
* @param id
* @return
*/
public int delete(Integer id);
}
线路service实现类
package com.lxs.travel.service.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.dao.RouteDao;
import com.lxs.travel.domain.Route;
import com.lxs.travel.service.RouteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RouteServiceImpl implements RouteService {
@Autowired
private RouteDao routeDao;
@Override
public PageInfo<Route> findPage(Route conditioin, int pageNum, int pageSize) {
return PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(() -> {
routeDao.find(conditioin);
});
}
@Override
public int add(Route route) {
return routeDao.insert(route);
}
@Override
public Route findById(Integer id) {
return routeDao.findById(id);
}
@Override
public int update(Route route) {
return routeDao.updateById(route);
}
@Override
public int delete(Integer id) {
return routeDao.deleteById(id);
}
}
测试
package com.lxs.travel.service.impl;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.domain.Route;
import org.junit.Test;
import com.lxs.travel.service.RouteService;
import junit.framework.TestCase;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RouteServiceImplTest extends TestCase {
@Autowired
private RouteService routeService;
@Test
public void findPage() {
Route condition = new Route();
condition.setRname("北京");
PageInfo<Route> page = routeService.findPage(condition, 1, 10);
page.getList().forEach((r) -> {
System.out.println(r.getRid() + "\t" + r.getRname() + "\t" + r.getCategory().getCname() + "\t" + r.getSeller().getSname() + "\t" + r.getRouteImgList().size());
});
}
@Test
public void findById() {
Route r = routeService.findById(34);
System.out.println(r.getRid() + "\t" + r.getRname() + "\t" + r.getCategory().getCname() +
"\t" + r.getSeller().getSname() + "\t" + r.getRouteImgList().size());
}
}
controller
package com.lxs.travel.controller;
import com.github.pagehelper.PageInfo;
import com.lxs.travel.domain.Category;
import com.lxs.travel.domain.Route;
import com.lxs.travel.domain.RouteImg;
import com.lxs.travel.domain.Seller;
import com.lxs.travel.service.CategoryService;
import com.lxs.travel.service.RouteImgService;
import com.lxs.travel.service.RouteService;
import com.lxs.travel.service.SellerService;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Controller
@RequestMapping("/admin/route/")
public class RouteController {
@Autowired
private RouteService routeService;
@Autowired
private CategoryService categoryService;
@Autowired
private SellerService sellerService;
@Autowired
private RouteImgService imgService;
/**
* 分页
* @param route* @param pageNum
* @param pageSize
* @param model
* @return
*/
@RequestMapping("/page")
public String page(
Route route,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
Model model) {
PageInfo<Route> page = routeService.findPage(route, pageNum, pageSize);
model.addAttribute("page", page);
//查询所有分类生成下拉框
List<Category> categories = categoryService.find();
model.addAttribute("categories", categories);
List<Seller> sellers = sellerService.find(new Seller());
model.addAttribute("sellers", sellers);
//用于页面回显
model.addAttribute("route", route);
return "route/list";
}
/**
* 删除
* @param id
* @return
*/
@RequestMapping("/delete/{id}")
public String delete(@PathVariable("id") Integer id) {
routeService.delete(id);
return "redirect:/admin/route/page";
}
/**
* 跳到添加页面
* @param model
* @return
*/
@RequestMapping("/toadd")
public String toAdd(Model model) {
//查询所有分类生成下拉框
List<Category> categories = categoryService.find();
model.addAttribute("categories", categories);
List<Seller> sellers = sellerService.find(new Seller());
model.addAttribute("sellers", sellers);
return "route/add";
}/**
* 执行添加
* @param route
* @param rimageFile
* @param request
* @return
* @throws IOException
*/
@RequestMapping("/doadd")
public String doAdd(Route route, @RequestParam("rimageFile") MultipartFile rimageFile, HttpServletRequest request) throws IOException {
performRImage(route, rimageFile, request);
routeService.add(route);
return "redirect:/admin/route/page";
}
private void performRImage(Route route, MultipartFile rimageFile, HttpServletRequest request) throws IOException {
//项目的部署的目录 + img/product/rimage/
String savePath = request.getServletContext().getRealPath("img/product/rimage/");
//处理文件名重复
String fileName= UUID.randomUUID().toString().replace("-","") + "." + FilenameUtils.getExtension(rimageFile.getOriginalFilename());
//上传目录不存在先创建
File savePathDir = new File(savePath);
if(!savePathDir.exists()){
savePathDir.mkdirs();
}
//保存文件
rimageFile.transferTo(new File(savePathDir,fileName));
//设置route的rimage属性=图片相对路径
route.setRimage("img/product/rimage/"+fileName);
}
/**
* 根据id查询,跳转到修改页面
* @param id
* @param model
* @return
*/
@RequestMapping("/toupdate/{id}")
public String toUpdate(@PathVariable Integer id, Model model) {
//查询所有分类生成下拉框
List<Category> categories = categoryService.find();
model.addAttribute("categories", categories);
List<Seller> sellers = sellerService.find(new Seller());
model.addAttribute("sellers", sellers);
Route route = routeService.findById(id);
model.addAttribute("route", route);
return "route/update";
}
@RequestMapping("/doupdate")
public String doUpdate(Route route, @RequestParam("rimageFile") MultipartFile rimageFile,HttpServletRequest request) throws IOException {
performRImage(route, rimageFile, request);
routeService.update(route);
return "redirect:/admin/route/page";
}
/**
* 根据id加载线路图片,跳到image.html
* @param id
* @param model
* @return
*/
@RequestMapping("/toimage/{id}")
public String toImage(@PathVariable("id") Integer id, Model model) {
Route route = routeService.findById(id);
model.addAttribute("route", route);
return "route/image";
}
@RequestMapping("/doimage")
public String doImage(
Integer rid,
@RequestParam("bigPicFile") MultipartFile[] bigPicFile,
@RequestParam("smallPicFile")MultipartFile[] smallPicFile,
HttpServletRequest request) throws Exception {
List<String> bigPic = new ArrayList<>();
List<String> smallPic = new ArrayList<>();
String path = request.getServletContext().getRealPath("/");
for (MultipartFile f : bigPicFile) {
File bigPath = new File(path + "img\\product\\big-pic\\");
if (!bigPath.exists()) {
bigPath.mkdirs();
}
String fileName = UUID.randomUUID().toString().replace("-", "") + "." + FilenameUtils.getExtension(f.getOriginalFilename());
f.transferTo(new File(bigPath, fileName));
bigPic.add("img/product/big-pic/" + fileName);
}
for (MultipartFile f : smallPicFile) {
File smallPath = new File(path + "img\\product\\small-pic\\");
if (!smallPath.exists()) {smallPath.mkdirs();
}
String fileName = UUID.randomUUID().toString().replace("-", "") + "." + FilenameUtils.getExtension(f.getOriginalFilename());
f.transferTo(new File(smallPath, fileName));
smallPic.add("img/product/small-pic/" + fileName);
}
//要添加的图片列表
List<RouteImg> ris = new ArrayList<>();
for (int i=0; i<bigPic.size(); i++) {
RouteImg img = new RouteImg();
img.setRid(rid);
img.setBigpic(bigPic.get(i));
img.setSmallpic(smallPic.get(i));
ris.add(img);
}
imgService.saveImg(rid, ris);
return "redirect:/admin/route/page";
}
}
主图(缩略图)上传
项目图片文件上传,注意重复文件名的处理
页面
页面使用基于bootstrap的图片上传插件fileinput
参考官方文档
bootstrap-fileinput是一款非常优秀的HTML5文件上传插件,支持文件预览、多选等一系列特性
一款非常优秀的HTML5文件上传插件,支持bootstrap 3.x 和4.x版本,具有非常多的特性:多文件选择。这个插件能最简单的帮你完成文件上传功能,且使用bootstrap样式。还支持多种文件的预览,images, text, html, video,audio, flash。另外还支持ajax方式上传文件,可以看到上传进度。支持拖拽的方式添加和删除文件
用法
如果你将一个 css class='file' 属性赋予 input 标签,插件将自动把字段 [input type="file"] 转换为文件输入控件。但是,如果你想通过 javascript 单独初始化插件,那么请勿将 css class='file' 属性附加到'input'上(因为这将导致重复的初始化,并且JavaScript代码可能会被跳过不执行)
步骤一:
<script src="/assets/js/bootstrap.min.js"></script>
<link href="/js/fileinput/css/fileinput.css" media="all" rel="stylesheet" type="text/css" />
<script src="/js/fileinput/js/fileinput.js" type="text/javascript"></script>
<script src="/js/fileinput/js/fileinput_locale_zh.js" type="text/javascript"></script>
步骤二:
方法一:在文件域中设置class="file"
<input id="rimageFile" name="rimageFile" class="file" type="file">
注意html的input属性multiple,决定fileinput是否接受多图片上传
方法二:使用js把文件域转换成fileinput对象
<input id="rimageFile" name="rimageFile" type="file" >
<script >
$(function(){
$("#rimageFile").fileinput({
});
})
</script>
修改
加载数据,调到修改页面
@RequestMapping("/toupdate/{id}")
public String toUpdate(@PathVariable Integer id, Model model) {
//查询所有分类生成下拉框
List<Category> categories = categoryService.find();
model.addAttribute("categories", categories);
List<Seller> sellers = sellerService.find(new Seller());
model.addAttribute("sellers", sellers);
Route route = routeService.findById(id);
model.addAttribute("route", route);
return "route/update";
}
修改回显主图(缩略图)到页面
<script th:inline="javascript">
$(function(){
var rimage = '/' + [[${route.rimage}]];
$("#rimageFile").fileinput({
initialPreview: [
"<img src='" + rimage +"' class='file-preview-image' >"
],
overwriteInitial: true
});
})
</script>
initialPreview:数组类型,指定初始化回显的图片列表
overwriteInitial:覆盖之前回显的内容
七:线路详细图片
需求: 一条旅游线路,有多张介绍线路的详细图片,详细图片分为小图和大图,前端选择小图可以预览大图
数据结构
service:
package com.lxs.travel.service;
import com.lxs.travel.domain.RouteImg;
import java.util.List;
public interface RouteImgService {
/**
* 处理图片
* @param rid 根据线路id 删除原图
* @param routeImgs 要添加的新图列表
*/
public void saveImg(Integer rid, List<RouteImg> routeImgs);
}
实现类:
package com.lxs.travel.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lxs.travel.dao.RouteImgDao;
import com.lxs.travel.domain.RouteImg;
import com.lxs.travel.service.RouteImgService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class RouteImgServiceImpl implements RouteImgService {
@Autowired
private RouteImgDao imgDao;
@Override
@Transactional
public void saveImg(Integer rid, List<RouteImg> routeImgs) {
imgDao.delete(Wrappers.<RouteImg>query().eq("rid",rid));
for (RouteImg routeImg : routeImgs) {
imgDao.insert(routeImg);
}
}
}
controller和页面
查看线路详细图片:
/**
* 根据id加载线路图片,跳到image.html
* @param id
* @param model
* @return
*/
@RequestMapping("/toimage/{id}")
public String toImage(@PathVariable("id") Integer id, Model model) {
Route route = routeService.findById(id);
model.addAttribute("route", route);
return "route/image";
}
页面回显image.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta charset="utf-8" />
<title>商品详细图片</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<!-- bootstrap & fontawesome -->
<link rel="stylesheet" href="/assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="/assets/font-awesome/4.5.0/css/font-awesome.min.css" />
<!-- page specific plugin styles -->
<!-- text fonts -->
<link rel="stylesheet" href="/assets/css/fonts.googleapis.com.css" />
<!-- ace styles -->
<link rel="stylesheet" href="/assets/css/ace.min.css" class="ace-main-stylesheet" id="main-ace-style" />
<!--[if lte IE 9]>
<link rel="stylesheet" href="/assets/css/ace-part2.min.css" class="ace-main-stylesheet" />
<![endif]-->
<link rel="stylesheet" href="/assets/css/ace-skins.min.css" />
<link rel="stylesheet" href="/assets/css/ace-rtl.min.css" />
<!--[if lte IE 9]>
<link rel="stylesheet" href="/assets/css/ace-ie.min.css" />
<![endif]-->
<!-- inline styles related to this page -->
<!-- ace settings handler -->
<script src="/assets/js/ace-extra.min.js"></script>
<!-- HTML5shiv and Respond.js for IE8 to support HTML5 elements and media queries -->
<!--[if lte IE 8]>
<script src="/assets/js/html5shiv.min.js"></script>
<script src="/assets/js/respond.min.js"></script>
<![endif]-->
<!--[if !IE]> -->
<script src="/assets/js/jquery-2.1.4.min.js"></script>
<!-- <![endif]-->
<!--[if IE]>
<script src="/assets/js/jquery-1.11.3.min.js"></script>
<![endif]-->
<script src="/assets/js/bootstrap.min.js"></script>
<!-- page specific plugin scripts -->
<script src="/assets/js/jquery.dataTables.min.js"></script>
<script src="/assets/js/jquery.dataTables.bootstrap.min.js"></script>
<script src="/assets/js/dataTables.buttons.min.js"></script>
<script src="/assets/js/buttons.flash.min.js"></script>
<script src="/assets/js/buttons.html5.min.js"></script>
<script src="/assets/js/buttons.print.min.js"></script>
<script src="/assets/js/buttons.colVis.min.js"></script>
<script src="/assets/js/dataTables.select.min.js"></script>
<!-- ace scripts -->
<script src="/assets/js/ace-elements.min.js"></script>
<script src="/assets/js/ace.min.js"></script>
<script language="javascript" type="text/javascript" src="/js/My97DatePicker/WdatePicker.js"></script>
<link href="/js/fileinput/css/fileinput.css" media="all" rel="stylesheet" type="text/css" />
<script src="/js/fileinput/js/fileinput.js" type="text/javascript"></script>
<script src="/js/fileinput/js/fileinput_locale_zh.js" type="text/javascript"></script>
</head>
<body class="no-skin">
<div th:replace="header :: navbar"></div>
<div class="main-container ace-save-state" id="main-container">
<script type="text/javascript">
try{ace.settings.loadState('main-container')}catch(e){}
</script>
<div th:replace="left :: sidebar"></div>
<div class="main-content">
<div class="main-content-inner">
<div class="breadcrumbs ace-save-state" id="breadcrumbs">
<ul class="breadcrumb">
<li>
<i class="ace-icon fa fa-home home-icon"></i>
<a href="#">首页</a>
</li>
<li>
<a href="#">用户</a>
</li>
<li class="active">用户管理</li>
</ul><!-- /.breadcrumb -->
</div>
<div class="page-content">
<form action="/admin/route/doimage" enctype="multipart/form-data" method="post">
<!-- TODO 处理隐藏域,记录线路ID -->
<input type="hidden" name="rid" id="rid" th:value="${route.rid}">
<div class="form-group">
<label for="bigPicFile">详细大图:</label>
<input id="bigPicFile" name="bigPicFile" type="file" multiple >
</div>
<div class="form-group">
<label for="smallPicFile">详细小图:</label>
<input id="smallPicFile" name="smallPicFile" type="file" multiple >
</div>
<div class="form-group" style="text-align: center">
<input class="btn btn-primary" type="submit" value="提交" />
<input class="btn btn-default" type="reset" value="重置" />
<input class="btn btn-default" type="button" value="返回" />
</div>
</form>
</div><!-- /.page-content -->
<!-- TODO 处理详细图片回显 -->
<script th:inline="javascript">
var ri = [[${route.routeImgList}]]; //后台查询的图片列表
var bi = []; //回显的大图数组
var si = []; //回显的小图数组
for (var i = 0; i < ri.length; i++) {
bi.push("<img src='/" + ri[i].bigpic +"' class='file-preview-image' >");
si.push("<img src='/" + ri[i].smallpic +"' class='file-preview-image' >");
}
$("#bigPicFile").fileinput({
initialPreview: bi,
overwriteInitial: true,
minFileCount: 1, //文件上传最少
maxFileCount: 4 //文件上传最多
});$("#smallPicFile").fileinput({
initialPreview: si,
overwriteInitial: true,
minFileCount: 1,
maxFileCount: 4
});
</script>
</div>
</div><!-- /.main-content -->
</div><!-- /.main-container -->
</body>
</html>
注意html5属性multiple,表示可以多文件上传
保存线路详细图片
@RequestMapping("/doimage")
public String doImage(
Integer rid,
@RequestParam("bigPicFile") MultipartFile[] bigPicFile,
@RequestParam("smallPicFile")MultipartFile[] smallPicFile,
HttpServletRequest request) throws Exception {
List<String> bigPic = new ArrayList<>();
List<String> smallPic = new ArrayList<>();
String path = request.getServletContext().getRealPath("/");
for (MultipartFile f : bigPicFile) {
File bigPath = new File(path + "img\\product\\big-pic\\");
if (!bigPath.exists()) {
bigPath.mkdirs();
}
String fileName = UUID.randomUUID().toString().replace("-", "") + "." + FilenameUtils.getExtension(f.getOriginalFilename());
f.transferTo(new File(bigPath, fileName));
bigPic.add("img/product/big-pic/" + fileName);
}
for (MultipartFile f : smallPicFile) {
File smallPath = new File(path + "img\\product\\small-pic\\");
if (!smallPath.exists()) {smallPath.mkdirs();
}
String fileName = UUID.randomUUID().toString().replace("-", "") + "." + FilenameUtils.getExtension(f.getOriginalFilename());
f.transferTo(new File(smallPath, fileName));
smallPic.add("img/product/small-pic/" + fileName);
}
//要添加的图片列表
List<RouteImg> ris = new ArrayList<>();
for (int i=0; i<bigPic.size(); i++) {
RouteImg img = new RouteImg();
img.setRid(rid);
img.setBigpic(bigPic.get(i));
img.setSmallpic(smallPic.get(i));
ris.add(img);
}
imgService.saveImg(rid, ris);
return "redirect:/admin/route/page";
}