【Springboot】信息管理逻辑分析

【Springboot】分类信息管理项目逻辑分析

  • (1)项目简介
      • (1.1)功能介绍
      • (1.2)版本介绍
      • (1.3)结构介绍
  • (2)第一步:准备工作
      • (2.1)准备静态资源(css、图片、js)
      • (2.2)准备配置文件application.properties
      • (2.3)准备日志文件log4j.properties
  • (3)第二步:包含的共通HTML
      • (3.1)导航栏
      • (3.2)页面共通的js函数
  • (4)第三步:业务处理
      • (4.1)Redis配置类
      • (4.2)实体类
      • (4.3)持久层Dao(JPA)
      • (4.4)业务层Service(Redis)
      • (4.5)后台管理页面跳转专用控制器AdminPageController
      • (4.6)专门用来提供RESTFUL服务器控制器CategoryController
      • (4.7)启动类Application
  • (5)其他类
      • (5.1)配置类CORSConfiguration.java(用于允许所有的请求都跨域)
      • (5.2)异常处理类GloabalExceptionHandler.java
      • (5.3)端口监测工具类PortUtil
  • (6)前端画面
      • (6.1)显示画面listCategory.html
      • (6.2)编辑画面editCategory.html
  • (7)分页
  • (8)业务流程

(1)项目简介

(1.1)功能介绍

  1. 对分类的产品进行增删改查
  2. 分页查询
  3. 上传图片
  4. Redis缓存

(1.2)版本介绍

  1. Springboot:1.5.9.RELEASE
  2. JDK:1.8.0_201
  3. Maven:
  4. Mysql:5.5.15
  5. Redis

(1.3)结构介绍

(2)第一步:准备工作

(2.1)准备静态资源(css、图片、js)

  1. 图片资源:在img目录下的site目录放着所有要用到的静态图片
  2. css:
    back:这个目录里有一个style.css,这是后台界面用到的样式
    bootstrap:这里存放的是bootstrap的css样式文件
    fore:这个目录里也有一个style.css,这是前台界面用到的样式
  3. js:
    /bootstrap:bootstrap用到的js文件
    /jquery: jquery用到的js文件
    /axios:ajax 库
    /moment:日期格式化库
    /vue :vue.js 库
    【Springboot】信息管理逻辑分析_第1张图片

(2.2)准备配置文件application.properties

#database
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tmall_springboot?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#表结构自动生成策略(none)
spring.jpa.hibernate.ddl-auto = none

#thymeleaf,使用 thymeleaf 作为视图
#LEGACYHTML5允许非严格的html出现,元素少点什么也可以编译通过
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
#cache=false 表示不要缓存,以免在开发过程中因为停留在缓存而给开发人员带来困扰
spring.thymeleaf.cache=false

#context
#上下文地址为 tmall_springboot, 所以访问的时候,都要加上这个
#比如:http://127.0.0.1:8080/tmall_springboot/admin
server.context-path=/tmall_springboot

#设置上传文件大小,默认只有1m
spring.http.multipart.maxFileSize=100Mb
spring.http.multipart.maxRequestSize=100Mb

#jpa对实体类的默认字段会把驼峰命名的属性
#转换为字段名的时候自动加上下划线。 这个配置的作用就是去掉下划线
#比如属性名称是 createDate, jpa 默认转换为字段名 create_Date
#有了这个配置之后,就会转换为同名字段 createDate
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

#显示 hibernate 执行的sql语句
#这个在上线之后,应该是关掉的,因为大量的 控制台输出会严重影响系统性能
#但是呢,因为本项目会和 redis 和 es 整合,打印 sql 语句的目的是为了观察 缓存是否起效果
spring.jpa.show-sql=true

(2.3)准备日志文件log4j.properties

log4j.rootLogger=debug, stdout, R

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log

log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=5

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

(3)第二步:包含的共通HTML

【Springboot】信息管理逻辑分析_第2张图片

(3.1)导航栏

在这里插入图片描述

<div class="navitagorDiv" th:fragment="html" xmlns:th="http://www.w3.org/1999/xhtml">
	<nav class="navbar navbar-default navbar-fixed-top navbar-inverse">
		<img style="margin-left:10px;margin-right:0px" class="pull-left" src="img/site/tmallbuy.png" height="45px">
		<a class="navbar-brand" href="#nowhere">天猫后台</a>
		
		<a class="navbar-brand" href="admin_category_list">分类管理</a>
		<a class="navbar-brand" href="admin_user_list">用户管理</a>
		<a class="navbar-brand" href="admin_order_list">订单管理</a>
	</nav>
</div>

(3.2)页面共通的js函数

  1. checkEmpty:判断值是否为空
  2. getUrlParms:获取地址栏参数的函数
  3. checkNumber:判断是否是数字
  4. checkInt:判断是否整数
  5. checkDeleteLink:确实是否要删除
  6. jump:分页跳转函数,向前跳或者向后跳,或者跳转到第一页或者最后一页
  7. jumpByNumber:分页跳转函数,跳转到指定页
<template th:fragment="html(title)" xmlns:th="http://www.w3.org/1999/xhtml">
	<!--2-用到的一系列的 js 和 css 文件-->
	<script src="js/jquery/2.0.0/jquery.min.js"></script>
	<link href="css/bootstrap/3.3.6/bootstrap.min.css" rel="stylesheet">
	<script src="js/bootstrap/3.3.6/bootstrap.min.js"></script>
	<script src="js/vue/2.5.16/vue.min.js"></script>
	<script src="js/axios/0.17.1/axios.min.js"></script>
	<script src="js/moment/2.22.2/moment.js"></script> <!-- vue.js 格式化日期用的 -->
	<link href="css/back/style.css" rel="stylesheet">
	<!--每个后台页面都在一开始使用了adminHeader.html-->
	
	<!--3-各种自定义函数,这些函数都会在后台管理页面上用到-->
	<script>
	//判断是否为空
	function checkEmpty(value,text){
		if(null==value || value.length==0){
			alert(text+ "不能为空");
			return false;
		}
		return true;
	}	
	
    //获取地址栏参数的函数
    function getUrlParms(para){
	    var search=location.search; //页面URL的查询部分字符串
	    var arrPara=new Array(); //参数数组。数组单项为包含参数名和参数值的字符串,如“para=value”
	    var arrVal=new Array(); //参数值数组。用于存储查找到的参数值
	 
	    if(search!=""){	
	        var index=0;
	        search=search.substr(1); //去除开头的“?”
	        arrPara=search.split("&");
	 
	        for(i in arrPara){
	            var paraPre=para+"="; //参数前缀。即参数名+“=”,如“para=”
	            if(arrPara[i].indexOf(paraPre)==0&& paraPre.length<arrPara[i].length){
	                arrVal[index]=decodeURI(arrPara[i].substr(paraPre.length)); //顺带URI解码避免出现乱码
	                index++;
	            }
	        }
	    }
	 
	    if(arrVal.length==1){
	        return arrVal[0];
	    }else if(arrVal.length==0){
	        return null;
	    }else{
	        return arrVal;
	    }
    }	
    
    //判断是否数字 (小数和整数)
	function checkNumber(value, text){
		
		if(value.length==0){
			alert(text+ "不能为空");
			return false;
		}
		if(isNaN(value)){
			alert(text+ "必须是数字");
			return false;
		}
		return true;
	}
    //判断是否整数
	function checkInt(value, text){
		
		if(value.length==0){
			alert(text+ "不能为空");
			return false;
		}
		if(parseInt(value)!=value){
			alert(text+ "必须是整数");
			return false;
		}
		return true;
	}
    //确实是否要删除
	function checkDeleteLink(){
		var confirmDelete = confirm("确认要删除");
		if(confirmDelete)
			return true;
		return false;		
	}
    //分页跳转函数,向前跳或者向后跳,或者跳转到第一页或者最后一页
    function jump(page,vue){
		if('first'== page && !vue.pagination.first)
			vue.list(0);
		
		else if('pre'== page &&	vue.pagination.hasPrevious )
			vue.list(vue.pagination.number-1);
		
		else if('next'== page && vue.pagination.hasNext)
			vue.list(vue.pagination.number+1);					
		
		else if('last'== page && !vue.pagination.last)
			vue.list(vue.pagination.totalPages-1);    	
    }
    //分页跳转函数,跳转到指定页
    function jumpByNumber(start,vue){
    	if(start!=vue.pagination.number)
			vue.list(start);       	
    }
	</script>	
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<!--把传递进来的 title 参数显示在 title 元素里-->
	<title th:text="${title}" ></title>
</template>

(4)第三步:业务处理

(4.1)Redis配置类

//配置 Redis, 这个配置的作用主要是使得保存在 redis 里的key和value转换为如图所示的具有可读性的字符串,否则会是乱码,很不便于观察。
@Configuration
//Redis 缓存配置类
public class RedisConfig extends CachingConfigurerSupport {
 
    @Bean
    public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {
        RedisSerializer stringSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.PUBLIC_ONLY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);  
         
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);         
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        CacheManager cacheManager = new RedisCacheManager(redisTemplate);
        return cacheManager;
  
    }
}

(4.2)实体类

  1. @Entity:表示这是一个实体类
  2. @Table(name = “category”):表示对应的表名是 category
@Entity//表示这是一个实体类
@Table(name = "category")//表示对应的表名是 category
//因为做前后端分离,前后端数据交互用的是json格式,那么Category对象就会被转换为json数据
//本项目使用jps来做实体咧的持久化,就会创造代理类来继承 Category ,并添加 handler 和 hibernateLazyInitializer 这两个无须 json 化的属性,所以这里需要用 JsonIgnoreProperties 把这两个属性忽略掉。
@JsonIgnoreProperties({ "handler","hibernateLazyInitializer" })
// @Data
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "id")//属性property:id对应上数据库列column:id
    int id;
    
    String name;
    
	public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

(4.3)持久层Dao(JPA)

使用JPA

// CategoryDAO 类集成了 JpaRepository,就提供了CRUD和分页 的各种常见功能。 这就是采用 JPA 方便的地方~
public interface CategoryDAO extends JpaRepository<Category,Integer>{

}

(4.4)业务层Service(Redis)

@Service//标记这个类是 Service类
@CacheConfig(cacheNames="categories")
public class CategoryService {
	@Autowired CategoryDAO categoryDAO;

	/**查询所有数据,并且实现分页
	 */
	@Cacheable(key="'categories-page-'+#p0+ '-' + #p1")
	public Page4Navigator<Category> list(int start, int size, int navigatePages) {
		//首先创建一个Sort对象,表示通过id倒排序
		Sort sort = new Sort(Sort.Direction.DESC, "id");
		Pageable pageable = new PageRequest(start, size,sort);
		//通过categoryDAO进行查询
		Page pageFromJPA =categoryDAO.findAll(pageable);
		//这里抛弃了 CategoryService 接口 加上 CategoryService 实现类的这种累赘的写法,而是直接使用 CategoryService 作为实现类来做。
		return new Page4Navigator<>(pageFromJPA,navigatePages);
	}
	
	/**查询所有数据
	 */
	@Cacheable(key="'categories-all'")
	public List<Category> list() {
    	Sort sort = new Sort(Sort.Direction.DESC, "id");
		return categoryDAO.findAll(sort);
	}

	/**添加数据
	 */
	@CacheEvict(allEntries=true)
	public void add(Category bean) {
		categoryDAO.save(bean);
	}

	/**删除数据
	 */
	@CacheEvict(allEntries=true)
	public void delete(int id) {
		categoryDAO.delete(id);
	}

	/**根据id查询单个数据
	 */
	@Cacheable(key="'categories-one-'+ #p0")
	public Category get(int id) {
		Category c= categoryDAO.findOne(id);
		return c;
	}

	/**更新数据
	 */
	@CacheEvict(allEntries=true)
	public void update(Category bean) {
		categoryDAO.save(bean);
	}
}

(4.5)后台管理页面跳转专用控制器AdminPageController

后台管理页面跳转专用控制器
因为是做前后端分离,所以数据是通过 RESTFUL接口来取的
而在业务上,除了 RESTFUL 服务要提供,还要提供页面跳转服务
所以所有的后台页面跳转都放在 AdminPageController 这个控制器里
而RSTFUL 专门放在 Category 对应的控制器 CategoryController.java 里面。

@Controller
public class AdminPageController {
	//路径为admin,客户端跳转到admin_category_list路径
	@GetMapping(value="/admin")
    public String admin(){
		return "redirect:admin_category_list";
    }

    //路径为admin_category_list,返回到admin文件夹下的listCategory.html
	@GetMapping(value="/admin_category_list")
	public String listCategory(){
		return "admin/listCategory";
	}

	//路径为admin_category_edit,返回到admin文件夹下的editCategory.html
	@GetMapping(value="/admin_category_edit")
	public String editCategory(){
		return "admin/editCategory";
	}
}

(4.6)专门用来提供RESTFUL服务器控制器CategoryController

对每个方法的返回值都会直接转换为 json 数据格式

@RestController
public class CategoryController {
   @Autowired
   CategoryService categoryService;

   //对于categories 访问,会获取所有的 Category对象集合,并返回这个集合
   //因为是声明为 @RestController, 所以这个集合,又会被自动转换为 JSON数组抛给浏览器。
   @GetMapping("/categories")
   //修改原 list 方法,接受 start 和 size 参数
   public Page4Navigator<Category> list(@RequestParam(value = "start", defaultValue = "0") int start, @RequestParam(value = "size", defaultValue = "5") int size) throws Exception {
       start = start<0?0:start;
       //返回的是 Page4Navigator 类型,并通过 RestController 转换为 json 对象抛给浏览器
       Page4Navigator<Category> page =categoryService.list(start, size, 5);  //5表示导航分页最多有5个,像 [1,2,3,4,5] 这样
       return page;
   }

   @PostMapping("/categories")
   public Object add(Category bean, MultipartFile image, HttpServletRequest request) throws Exception {
       categoryService.add(bean);
       saveOrUpdateImageFile(bean, image, request);
       return bean;
   }

   public void saveOrUpdateImageFile(Category bean, MultipartFile image, HttpServletRequest request)
           throws IOException {
       File imageFolder= new File(request.getServletContext().getRealPath("img/category"));
       File file = new File(imageFolder,bean.getId()+".jpg");
       if(!file.getParentFile().exists())
           file.getParentFile().mkdirs();
       image.transferTo(file);
       BufferedImage img = ImageUtil.change2jpg(file);
       ImageIO.write(img, "jpg", file);
   }

   @DeleteMapping("/categories/{id}")
   public String delete(@PathVariable("id") int id, HttpServletRequest request)  throws Exception {
       categoryService.delete(id);
       File  imageFolder= new File(request.getServletContext().getRealPath("img/category"));
       File file = new File(imageFolder,id+".jpg");
       file.delete();
       return null;
   }

   @GetMapping("/categories/{id}")
   public Category get(@PathVariable("id") int id) throws Exception {
       Category bean=categoryService.get(id);
       return bean;
   }

   @PutMapping("/categories/{id}")
   public Object update(Category bean, MultipartFile image,HttpServletRequest request) throws Exception {
       String name = request.getParameter("name");
       bean.setName(name);
       categoryService.update(bean);

       if(image!=null) {
           saveOrUpdateImageFile(bean, image, request);
       }
       return bean;
   }

}

(4.7)启动类Application

@SpringBootApplication
@EnableCaching//Redis使用增加注解,用来启动缓存
public class Application {
    //检查端口6379是否启动,就是Redis服务器使用的端口,如果没有启动就会退出springboot
    static {
        PortUtil.checkPort(6379,"Redis 服务端",true);
    }
    public static void main(String[] args) {
    	SpringApplication.run(Application.class, args);
    }
}

(5)其他类

(5.1)配置类CORSConfiguration.java(用于允许所有的请求都跨域)

因为是二次请求,第一次是获取 html 页面, 第二次通过 html 页面上的 js 代码异步获取数据,一旦部署到服务器就容易面临跨域请求问题,所以允许所有访问都跨域,就不会出现通过 ajax 获取数据获取不到的问题了。

@Configuration
public class CORSConfiguration extends WebMvcConfigurerAdapter{
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		//所有请求都允许跨域
		registry.addMapping("/**")
				.allowedOrigins("*")
				.allowedMethods("*")
				.allowedHeaders("*");
	}
}

(5.2)异常处理类GloabalExceptionHandler.java

主要是在处理删除父类信息的时候,因为外键约束的存在,而导致违反约束。

@RestController
@ControllerAdvice
public class GloabalExceptionHandler {
    @ExceptionHandler(value = Exception.class)
    public String defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
    	e.printStackTrace();
    	Class constraintViolationException = Class.forName("org.hibernate.exception.ConstraintViolationException");
    	if(null!=e.getCause()  && constraintViolationException==e.getCause().getClass()) {
    		return "违反了约束,多半是外键约束";
    	}
        return e.getMessage();
    }
}

(5.3)端口监测工具类PortUtil

用于判断某个端口是否启动。 因为常常忘记启动 redis服务器,而导致系统无法运行, 这个工具的作用,是帮助检查是否启动了,在启动类中使用

public class ImageUtil {
  
    public static BufferedImage change2jpg(File f) {
        try {
            Image i = Toolkit.getDefaultToolkit().createImage(f.getAbsolutePath());
            PixelGrabber pg = new PixelGrabber(i, 0, 0, -1, -1, true);
            pg.grabPixels();
            int width = pg.getWidth(), height = pg.getHeight();
            final int[] RGB_MASKS = { 0xFF0000, 0xFF00, 0xFF };
            final ColorModel RGB_OPAQUE = new DirectColorModel(32, RGB_MASKS[0], RGB_MASKS[1], RGB_MASKS[2]);
            DataBuffer buffer = new DataBufferInt((int[]) pg.getPixels(), pg.getWidth() * pg.getHeight());
            WritableRaster raster = Raster.createPackedRaster(buffer, width, height, width, RGB_MASKS, null);
            BufferedImage img = new BufferedImage(RGB_OPAQUE, raster, false, null);
            return img;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        }
    }
  
    public static void resizeImage(File srcFile, int width,int height, File destFile) {
        try {
            if(!destFile.getParentFile().exists())
                destFile.getParentFile().mkdirs();
            Image i = ImageIO.read(srcFile);
            i = resizeImage(i, width, height);
            ImageIO.write((RenderedImage) i, "jpg", destFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
      
    public static Image resizeImage(Image srcImage, int width, int height) {
        try {
            BufferedImage buffImg = null;
            buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            buffImg.getGraphics().drawImage(srcImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
  
            return buffImg;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

(6)前端画面

(6.1)显示画面listCategory.html

<!--分类查询对应的html文件-->
<!--一共用到了4个公共包含文件-->
<!--1-分类管理-->
<head th:include="include/admin/adminHeader::html('分类管理')" ></head>
<body>
<!--2-导航栏-->
<div th:replace="include/admin/adminNavigator::html" ></div>
<script>

    <!--获取数据-->
    $(function(){
        //这个是jquery的代码,表示当整个html加载好了之后执行
        var data4Vue = {
            //vue用到的数据, uri表示访问哪个地址去获取数据,这里的值是categories
            //和CategoryController.java 相呼应
            uri:'categories',
            beans: [],
            bean: { id: 0, name: ''},
            pagination:{},
            file: null
        };

        //ViewModel
        //创建Vue对象
        //一个Vue对象有4部分,el是关联视图,data是传递的参数,mounted是vue加载成功后的执行,moethods是方法群
        var vue = new Vue({
            //el表示和本页面的 
元素绑定 el: '#workingArea', //data表示vue 使用上面的data4Vue对象。 data: data4Vue, //加载Vue对象成功之后会调用,成功的时候去调用 list() 函数 mounted:function(){ //mounted表示这个 Vue 对象加载成功了 this.list(0); }, methods: { //list 函数使用 data4Vue里的 uri作为地址,然后调用 axios.js 这个 ajax库,进行异步调用 list:function(start){ var url = this.uri+ "?start="+start; axios.get(url).then(function(response) { vue.pagination = response.data; //调用成功之后,把服务端返回的数据,保存在 vue.beans 上 vue.beans = response.data.content; }); }, add: function () { if(!checkEmpty(this.bean.name, "分类名称")) return; if(!checkEmpty(this.file, "分类图片")) return; var url = this.uri; //axios.js 上传文件要用 formData 这种方式 var formData = new FormData(); formData.append("image", this.file); formData.append("name", this.bean.name); axios.post(url,formData).then(function(response){ vue.list(0); vue.bean = { id: 0, name: '', hp: '0'}; $("#categoryPic").val(''); vue.file = null; }); }, deleteBean: function (id) { if(!checkDeleteLink()) return; var url = this.uri+"/"+id; axios.delete(url).then(function(response){ if(0!=response.data.length){ alert(response.data); } else{ vue.list(0); } }); }, getFile: function (event) { this.file = event.target.files[0]; }, //增加了两个跳转方法,分别是 jump和 jumpByNumber,而这两个方法的定义在 前面讲解的 adminHeader.html 里 jump: function(page){ jump(page,vue); //定义在adminHeader.html 中 }, jumpByNumber: function(start){ jumpByNumber(start,vue); } } }); }); //上面是获取数据,这里就是显示数据 </script> <div id="workingArea" > <h1 class="label label-info" >分类管理</h1> <br> <br> <div class="listDataTableDiv"> <table class="table table-striped table-bordered table-hover table-condensed"> <thead> <tr class="success"> <th>ID</th> <th>图片</th> <th>分类名称</th> <th>属性管理</th> <th>产品管理</th> <th>编辑</th> <th>删除</th> </tr> </thead> <tbody> <!--使用 v-for进行遍历, 这个 beans 就表示data4Vue里面的beans属性--> <tr v-for="bean in beans "> <!--bean就是遍历出来的每个id, 这里就是输出每个分类的id.--> <td>{{bean.id}}</td> <td> <img height="40px" :src="'img/category/'+bean.id+'.jpg'"> </td> <td> {{bean.name}} </td> <td> <!--在超链里的href里拼接分类id.--> <a :href="'admin_property_list?cid=' + bean.id "><span class="glyphicon glyphicon-th-list"></span></a> </td> <td> <a :href="'admin_product_list?cid=' + bean.id "><span class="glyphicon glyphicon-shopping-cart"></span></a> </td> <td> <a :href="'admin_category_edit?id=' + bean.id "><span class="glyphicon glyphicon-edit"></span></a> </td> <td> <a href="#nowhere" @click="deleteBean(bean.id)"><span class="glyphicon glyphicon-trash"></span></a> </td> </tr> </tbody> </table> </div> <!--3-分页页面--> <div th:replace="include/admin/adminPage::html" ></div> <div class="panel panel-warning addDiv"> <div class="panel-heading">新增分类</div> <div class="panel-body"> <table class="addTable"> <tr> <td>分类名称</td> <td><input @keyup.enter="add" v-model.trim="bean.name" type="text" class="form-control"></td> </tr> <tr> <td>分类图片</td> <td> <input id="categoryPic" accept="image/*" type="file" name="image" @change="getFile($event)" /> </td> </tr> <tr class="submitTR"> <td colspan="2" align="center"> <a href="#nowhere" @click="add" class="btn btn-success">提交</a> </td> </tr> </table> </div> </div> </div> <!--4-底部显示--> <div th:replace="include/admin/adminFooter::html" ></div> </body> </html>

(6.2)编辑画面editCategory.html

<!--一个项目里面有很多可以共用的部分,使用包含技术可以把这些共用部分包含起来-->
<head th:include="include/admin/adminHeader::html('编辑分类')" ></head>
<body>
<div th:replace="include/admin/adminNavigator::html" ></div>
<script>
    $(function(){
        var data4Vue = {
            uri: 'categories',
            listURL:'admin_category_list',
            bean: { id: 0, name: '', hp: '0'},
            file:''
        };

        //ViewModel
        var vue = new Vue({
            el: '#workingArea',
            data: data4Vue,
            mounted:function(){ //mounted 表示这个 Vue 对象加载成功了
                this.get();
            },
            methods: {
                get:function(){
                    var id = getUrlParms("id");
                    var url = this.uri+"/"+id;
                    axios.get(url).then(function(response) {
                        vue.bean = response.data;
                    })
                },
                update:function () {
                    if(!checkEmpty(this.bean.name, "分类名称"))
                        return;
                    var url = this.uri+"/"+this.bean.id;

                    //axios.js 上传文件要用 formData 这种方式
                    var formData = new FormData();
                    formData.append("image", this.file);
                    formData.append("name", this.bean.name);
                    axios.put(url,formData).then(function(response){
                        location.href=vue.listURL;
                    });
                },
                getFile: function (event) {
                    this.file = event.target.files[0];
                }
            }
        });
    });
</script>

<div id="workingArea">

    <ol class="breadcrumb">
        <li><a href="admin_category_list">所有分类</a></li>
        <li class="active">编辑分类</li>
    </ol>

    <div class="panel panel-warning editDiv">
        <div class="panel-heading">编辑分类</div>
        <div class="panel-body">
            <table class="editTable">
                <tr>
                    <td>分类名称</td>
                    <td><input  @keyup.enter="update" v-model.trim="bean.name" type="text" class="form-control"></td>
                </tr>
                <tr>
                    <td>分类图片</td>
                    <td>
                        <input id="categoryPic" accept="image/*" type="file" name="image" @change="getFile($event)" />
                    </td>
                </tr>
                <tr class="submitTR">
                    <td colspan="2" align="center">
                        <input type="hidden" name="id"   v-model.trim="bean.id" >
                        <a href="#nowhere" class="btn btn-success" @click="update">提 交</a>
                    </td>
                </tr>
            </table>
        </div>
    </div>
</div>

<div th:replace="include/admin/adminFooter::html" ></div>
</body>

(7)分页

(8)业务流程

【Springboot】信息管理逻辑分析_第3张图片

你可能感兴趣的:(框架和中间件)