1 大章列表查询后端模块
这里首先新建一个 Maven 模块,取名 busines 。这个模块的具体功能是实现大章列表的查询,在模块中会有启动类和 controller 层,controller 层是前端调用后端的接口,controller 层通过 server 模块中的 service 层和 mybatis-generator 代码生成的 XML 查询语句得到对应的数据,并将其通过 dto 数据传输类返回到前端。
从前面的描述可以知道,首先我们需要完成 business 模块的内容填充;然后用 mybatis-generator 生成 domain 实体类、Mapper 查询接口和包含 SQL 语句的 XML 文件;在 server 模块中新建 dto 数据传输层;最后为了测试功能会建立一个数据库表并添加测试字段。
1 填充 business 模块
最终完成后的结构如下图,可以自己尝试一下照着 system 模块写一下。
这里把 ChapterController 放到了新的包里是为了区别后面不同用户登陆后的区别。
business 模块各部分代码如下:
BusinessApplication.java
package com.course.business.config;import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;@SpringBootApplication
@EnableEurekaClient
@ComponentScan("com.course")
@MapperScan("com.course.server.mapper")
public class BusinessApplication {private static final Logger LOG = LoggerFactory.getLogger(BusinessApplication.class);// public static void main(String[] args) {
// SpringApplication.run(EurekaApplication.class, args);
// }public static void main(String[] args){SpringApplication app = new SpringApplication(BusinessApplication.class);Environment env = app.run(args).getEnvironment();LOG.info("启动成功!");LOG.info("Business地址:\t http://127.0.0.1:{}",env.getProperty("server.port"));}}
ChapterController.java 这里的返回类型已经对应了后面添加 dto 层的时态
package com.course.business.controller.admin;import com.course.server.domain.Chapter;
import com.course.server.dto.ChapterDto;
import com.course.server.service.ChapterService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.List;@RestController
@RequestMapping("/admin")
public class ChapterController {@Resourceprivate ChapterService chapterService;@RequestMapping("/chapter")public List<ChapterDto> chapter(){return chapterService.list();}
}
application.properties 端口号加一
spring.application.name=business
server.port=9002server.servlet.context-path=/businesseureka.client.service-url.defaultZone=http://localhost:8761/eureka/
pom.xml 将 system 模块中引入的 dependencies 加入。
2 mybatis-generator 生成代码
将最下面的<table tableName="test" domainObjectName="Test"></table>
修改为<table tableName="chapter" domainObjectName="Chapter"></table>
,然后运行 mybatis-generator,可以看到 server 模块中的 domain 层、mapper 层和 resources/mapper 中都生成了对应的代码。
这里 service 层需要手动写一下,具体只需要将 test 改为 chapter,Test 改为 Chapter 即可。
3 添加 dto 数据传输层
因为有时我们不需要持久层中的所有信息,如果需要修改传输的部分信息需要在对应的所有地方修改一遍,这样效率就很低。于是,我们新建了一个 dto 数据传输层,如果需要对数据进行一定的处理直接在这个类中修改即可,节省了许多时间。
这里因为并不需要对持久层的 chapter 进行什么处理,就直接复制到 dto 包中。
前面 business/controller 层我们已经将返回的数据类型改为 ChapterDto 类型,所以有些类会报错,先修改 server/service 层,将每个得到的 Chapter 对象进行处理后得到 ChapterDto 类型的对象返回给控制层,这里如果只是选择部分数据传回可以使用 BeanUtils 中的 copyProperties 方法。
package com.course.server.service;import com.course.server.domain.Chapter;
import com.course.server.domain.ChapterExample;
import com.course.server.dto.ChapterDto;
import com.course.server.mapper.ChapterMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;@Service
public class ChapterService {@Resourceprivate ChapterMapper chapterMapper;public List<ChapterDto> list(){ChapterExample chapterExample = new ChapterExample();List<Chapter> chapterList = chapterMapper.selectByExample(chapterExample);List<ChapterDto> chapterDtosList = new ArrayList<>();for (int i = 0; i < chapterList.size(); i++) {Chapter chapter = chapterList.get(i);ChapterDto chapterDto = new ChapterDto();BeanUtils.copyProperties(chapter,chapterDto);chapterDtosList.add(chapterDto);}return chapterDtosList;};
}
这里修改过后一些类因为类型问题会报红,把 Chapter 修改为 ChapterDto 就行。
4 新建数据库表并测试 business 模块
这里用之前新建的 doc/db/all.sql 来建新表和添加数据,用 MySQL 和 Navicat也可以。在 all.sql 运行下面语句。
drop table if exists `chapter`;
create table `chapter` (`id` char(8) not null comment 'ID',`course_id` char(8) comment '课程ID',`name` varchar(50) comment '名称',primary key(`id`)
)engine=innodb default charset=utf8mb4 comment='大章';insert into chapter values ('00000000','00000001','测试第一章');
insert into chapter values ('00000001','00000001','测试第一章');
接着启动 eureka 和 business ,进入后http://127.0.0.1:9002/business/admin/chapter
显示如下图片,说明模块没有问题。
2 大章列表查询前端页面
在这一部分需要处理的有新建 chapter 对应的前端页面,修改路由配置。
1 新建 chapter 页面
在 admin 包中新建一个 chapte.vue 文件,这里是大章查询,主要是为了查询大章并将结果显示。所以在 ace 模板中找到了 table.html ,将页面的一个表格部分放到了 chapter.vue 页面中。
这时 chapter.vue 代码如下:
<template><table id="simple-table" class="table table-bordered table-hover"...></template>
2 修改路由
之前在 login 页面登陆后会进入到 admin 页面,但是业务逻辑应该是点击 login 后会进入到 welcome 页面,并且 admin 页面对于用户来说是透明的。所以在这里会修改下路由,让登陆后会直接跳到 welcome,并且在请求路径中不会显示出 admin。
首先在 login.vue 修改跳转路径
<script>export default {name: "login",mounted: function(){$('body').removeClass('no-skin');$('body').attr('class', 'login-layout light-login');},methods: {login(){this.$router.push("/welcome")}}}
</script>
然后在 router.js 添加 chapter 页面并删除请求路径中到 /admin,然后在 admin 包含的页面中添加 name 属性,方便后序页面跳转的逻辑。
import Vue from "vue"
import Router from "vue-router"
import Login from "./views/login.vue"
import Admin from "./views/admin"
import Welcome from "./views/admin/welcome"
import Chapter from "./views/admin/chapter"Vue.use(Router)export default new Router({mode: "history",base: process.env.BASE_URL,routes: [{path: "*",redirect: "/login",}, {path: "/login",component: Login,}, {path: "/",name: "admin",component: Admin,children: [{path: "welcome",name: "welcome",component: Welcome,}, {path: "business/chapter",name: "business/chapter",component: Chapter,}]}]
})
最后在 admin.vue 中添加菜单栏中 welocme 和 大章管理 的跳转功能和激活样式。
主要修改是将 herf 属性修改为 <router-link to="/..">
,下面对应的</router-link>
,还有上面增加 id="welcome-sidebar"
,增加的 id 是为了增加激活样式。
同样的修改下面的 大章管理 ,最后 nav-list
部分的代码如下:
<ul class="nav nav-list"><li class="" id="welcome-sidebar"><router-link to="/welcome"><i class="menu-icon fa fa-tachometer"></i><span class="menu-text"> welcome </span></router-link><b class="arrow"></b></li><li class=""><a href="#" class="dropdown-toggle"><i class="menu-icon fa fa-file-o"></i><span class="menu-text">系统管理<span class="badge badge-primary">5</span></span><b class="arrow fa fa-angle-down"></b></a><b class="arrow"></b><ul class="submenu"><li class=""><a href="faq.html"><i class="menu-icon fa fa-caret-right"></i>用户管理</a><b class="arrow"></b></li><li class=""><a href="error-404.html"><i class="menu-icon fa fa-caret-right"></i>权限管理</a><b class="arrow"></b></li></ul></li><li class="active open"><a href="#" class="dropdown-toggle"><i class="menu-icon fa fa-file-o"></i><span class="menu-text">业务管理<span class="badge badge-primary">5</span></span><b class="arrow fa fa-angle-down"></b></a><b class="arrow"></b><ul class="submenu"><li class="active" id="business-chapter-sidebar"><router-link to="/business/chapter"><i class="menu-icon fa fa-caret-right"></i>大章管理</router-link><b class="arrow"></b></li></ul></li></ul><!-- /.nav-list -->
admin.vue 中增加激活样式代码如下,activeSidebar 函数作用是改变激活样式,让当前点击的组件显示激活。
<script>export default {name: "admin",mounted: function(){let _this = this;$('body').removeClass('login-layout light-login');$('body').attr('class', 'no-skin');_this.activeSidebar(_this.$route.name.replace("/","-")+"-sidebar");},methods: {activeSidebar: function (id) {$("#"+id).siblings().removeClass("active");$("#"+id).siblings().find("li").removeClass("active");$("#"+id).addClass("active");let parentLi = $("#"+id).parents("li");if(parentLi){parentLi.siblings().removeClass("active open");parentLi.addClass("active open");}}},watch:{$route:{handler: function () {let _this = this;_this.$nextTick(function () {_this.activeSidebar(_this.$route.name.replace("/","-")+"-sidebar");})}}}}
</script>
3 解决前后端交互
前面已经把单表查询的前端页面和后端模块完成了,现在需要完成前后端的交互,这里用到的事 axios 来进行前后端的交互;此时如果后端向前端传递数据会产生跨域的问题,这里把处理类放在 gateway 模块下面;之前用的 table 的格式也需要对应后端的数据进行一下改动。
1 前端使用 axios
首先需要安装 axios ,terminal 进入 admin 中运行npm install axios --save
下载并依赖 axios ,然后到 main.js 中引入。
main.js 代码如下:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import axios from 'axios'Vue.config.productionTip = false
Vue.prototype.$ajax = axiosnew Vue({router,render: h => h(App),
}).$mount('#app')
接着在 chapter.vue 中使用 axios 请求数据,代码如下:
<script>export default {name: "chapter",mounted: function(){let _this = this;_this.list();},methods: {list() {let _this = this;_this.$ajax.get('http://127.0.0.1:9002/business/admin/chapter/list').then((response)=>{console.log(response);})}}}
</script>
这里可以在控制台看到因为跨域报错了,但是请求应该是成功了。
2 解决跨域问题
在 GatewayApplication 中我们添加跨域配置代码:
//跨域配置@Beanpublic CorsWebFilter CrosFilter(){CorsConfiguration config = new CorsConfiguration();config.setAllowCredentials(Boolean.TRUE);config.addAllowedMethod("*");config.addAllowedOrigin("*");config.addAllowedHeader("*");config.setMaxAge(3600L);UrlBasedCorsConfigurationSource source= new UrlBasedCorsConfigurationSource(new PathPatternParser());source.registerCorsConfiguration("/**",config);return new CorsWebFilter(source);}
同时也可以选择在 server 中配置:
package com.course.server.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;//@Configuration
//public class CorsConfig implements WebMvcConfigurer {
// @Override
// public void addCorsMappings(CorsRegistry registry) {
// registry.addMapping("/**")
// .allowedOrigins("*")
// .allowedHeaders(CorsConfiguration.ALL)
// .allowedMethods(CorsConfiguration.ALL)
// .allowCredentials(true)
// .maxAge(3600);
// }
//}
我选择了在 gateway 中配置,所以还要在 application.properties 中增加路由转发设置:
spring.cloud.gateway.routes[1].id=business
spring.cloud.gateway.routes[1].uri=http://127.0.0.1:9002
#spring.cloud.gateway.routes[1].uri=lb://business
spring.cloud.gateway.routes[1].predicates[0].name=Path
spring.cloud.gateway.routes[1].predicates[0].args[0]=/business/**
如果不用 gateway 设置跨域,chapter 中的请求路径就不需要改变,我这里把请求路径中的端口 9002 改为了 9000。
刷新页面,这时成功取得后端的数据:
3 修改 table 样式
这里我们不需要显示之前的 checkbox、details 之类的数据,我们把表头先分别修改成 id、名称 和 课程,后面的操作按钮留下来。然后在表身中用一个 v-for 循环显示所有数据。
chapter最终代码如下:
<template><table id="simple-table" class="table table-bordered table-hover"><thead><tr><th>ID</th><th>名称</th><th>课程</th><th>操作</th></tr></thead><tbody><tr v-for="chapter in chapters" v-bind:key="chapter"><td>{{chapter.id}}</td><td>{{chapter.name}}</td><td>{{chapter.courseId}}</td><td><div class="hidden-sm hidden-xs btn-group"><button class="btn btn-xs btn-success"><i class="ace-icon fa fa-check bigger-120"></i></button><button class="btn btn-xs btn-info"><i class="ace-icon fa fa-pencil bigger-120"></i></button><button class="btn btn-xs btn-danger"><i class="ace-icon fa fa-trash-o bigger-120"></i></button><button class="btn btn-xs btn-warning"><i class="ace-icon fa fa-flag bigger-120"></i></button></div><div class="hidden-md hidden-lg"><div class="inline pos-rel"><button class="btn btn-minier btn-primary dropdown-toggle" data-toggle="dropdown" data-position="auto"><i class="ace-icon fa fa-cog icon-only bigger-110"></i></button><ul class="dropdown-menu dropdown-only-icon dropdown-yellow dropdown-menu-right dropdown-caret dropdown-close"><li><a href="#" class="tooltip-info" data-rel="tooltip" title="View"><span class="blue"><i class="ace-icon fa fa-search-plus bigger-120"></i></span></a></li><li><a href="#" class="tooltip-success" data-rel="tooltip" title="Edit"><span class="green"><i class="ace-icon fa fa-pencil-square-o bigger-120"></i></span></a></li><li><a href="#" class="tooltip-error" data-rel="tooltip" title="Delete"><span class="red"><i class="ace-icon fa fa-trash-o bigger-120"></i></span></a></li></ul></div></div></td></tr></tbody></table></template><script>export default {name: "chapter",data: function(){return{chapters:[]}},mounted: function(){let _this = this;_this.list();},methods: {list() {let _this = this;_this.$ajax.get('http://127.0.0.1:9000/business/admin/chapter/list').then((response)=>{console.log(response);_this.chapters = response.data;})}}}
</script>
最后,我们完成了单表页面查询的前端页面,后端查询和前后端交互的工作。