1.前端集成axios,npm安装,前后端交互
asus@LAPTOP-CQRDCFKL MINGW64 /d/DEV_CODE/Intelligy_idead_code/spring/springcloud/yameng-cevent-source-cloudcenter/cevent-source-cloudcenter/cevent-ymcms-admin (master)
$ npm install axios --save-dev #安装生产/开发插件及依赖= -D
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.2 (node_modulesfsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_moduleswatchpack-chokidar2node_modulesfsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_moduleswebpack-dev-servernode_modulesfsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
+ axios@0.21.1
added 1 package from 1 contributor in 10.191s
63 packages are looking for funding
run `npm fund` for details
axios
2.axios调用后端接口后,浏览器 cors (cross origin resource sharing跨原始站点资源共享=跨域问题
postman测试后端接口
请求CORS跨域
3.后端解决跨域:server-common公共跨域配置config
package cevent.source.cloudcenter.server.config;/**
* Created by Cevent on 2021/3/14.
*/
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;
/**
* @author cevent
* @description 跨域问题CORS-cross origin resource sharing跨原始站点资源共享问题,即跨域问题
* @date 2021/3/14 17:24
*/@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//请求路径限制
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedHeaders(CorsConfiguration.ALL)
.allowedMethods(CorsConfiguration.ALL)
.allowCredentials(true)
.maxAge(3600);//1小时无需再次检测(发送OPTIONS请求)
}
}
异常apache异常: annot contain the special value “*”
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value “*” since that cannot be set on the “Access-Control-Allow-Origin” response header. To allow credentials to a set of origins, list them explicitly or consider using “allowedOriginPatterns” instead.
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
springboot版本问题,需更新originPatterns
浏览器跨域CORS解决
4.前端module遍历后端响应list
module遍历后端传入数据解构
实现列表查询效果图
5.gateway路由跨域CORS配置SpringApplication
package cevent.source.cloudcenter.gateway;/**
* Created by Cevent on 2021/2/19.
*/
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.Bean;
import org.springframework.core.env.Environment;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* @author cevent
* @description
* @date 2021/2/19 15:54
*/@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
private static final Logger LOG= LoggerFactory.getLogger(GatewayApplication.class);
public static void main(String[] args) {
SpringApplication application=new SpringApplication(GatewayApplication.class);
Environment environment=application.run(args).getEnvironment();
LOG.info("路由模块gateway启动");
LOG.info("gateway地址:t {}",environment.getProperty("server.port"));
}
/**
* CORS-cross origin resource sharing跨域配置
*/ @Bean
public CorsWebFilter corsWebFilter(){
CorsConfiguration corsConfiguration=new CorsConfiguration();
corsConfiguration.setAllowCredentials(Boolean.TRUE);
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedHeader("*");
//注意springboot新版本取消了addAllowedOrigin,要使用addAllowedOriginPattern
corsConfiguration.addAllowedOriginPattern("*");
corsConfiguration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource corsConfigurationSource=new UrlBasedCorsConfigurationSource(new PathPatternParser());
corsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);
//返回对象CorsWebFilter
return new CorsWebFilter(corsConfigurationSource);
}
}
6.common中注释springboot单应用cors跨域配置
CORS-springboot单应用注销
注解CORS
axios请求:this.$axios.get(‘ ‘).then((resp)=>{ resp.data })
axios请求成功
7.扩展:集成mybatis分页插件pageHelper
- 主pom引入github.pageHelper
<!--集成mybatis-pageHelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.10</version>
</dependency>
- server-common继承
引入主pom
- server-common的 modules ervice增加page分页方法
ForEach与for方法对比
forEach->module
//3.分页查询:pageHelper测试
public List<ModuleDto> moduleDtoListPage(){
PageHelper.startPage(1,5);
ModuleExample moduleExample=new ModuleExample();
moduleExample.setOrderByClause("uni_id desc");
List<Module> moduleList=moduleMapper.selectByExample(moduleExample);
List<ModuleDto> moduleDtoList=new ArrayList<>();
//forEach方法
moduleList.stream().forEach(module->{
ModuleDto moduleDto=new ModuleDto();
BeanUtils.copyProperties(module,moduleDto);
moduleDtoList.add(moduleDto);
});
return moduleDtoList;
}
- business的controller新增分页请求
//3.分页请求
@RequestMapping("/dtoListPage")
public List<ModuleDto> getDtoPage(){
return moduleService.moduleDtoListPage();
}
- postman测试
分页测试
日志输出
分页方法前端测试
- server-common分页dto配置
controller-pageDto
- server-common分页service生成前端传参,后端pageInfo处理存入pageDto=》list数组对象
/*4.分页查询:pageDto保存数据
list数据集合:listPageData
当前页码:currentPage
每页行数:size
总计函数:totalSize
前端可返回pageDto,无需返回值
*/public void getModulePageDtoList(PageDto pageDto){
//1.分页设置(前端传入当前页和每页行数params),起始页,每页行数size
PageHelper.startPage(pageDto.getCurrentPage(),pageDto.getSize());
ModuleExample moduleExample=new ModuleExample();
moduleExample.setOrderByClause("id asc");
List<Module> moduleList=moduleMapper.selectByExample(moduleExample);
//2.将mapper查询结果复制到pageInfo
PageInfo<Module> pageInfo=new PageInfo<>(moduleList);
//3.pageInfo计算处理处理=>后分页的总行数set到pageDto做记录
pageDto.setTotalSize(pageInfo.getTotal());
List<ModuleDto> moduleDtoList=new ArrayList<>();
moduleList.stream().forEach(module->{
ModuleDto moduleDto=new ModuleDto();
BeanUtils.copyProperties(module,moduleDto);
moduleDtoList.add(moduleDto);
});
//4.将dtoList放入pageDto的listPageData
pageDto.setListPageData(moduleDtoList);
}
- server-common分页-business-controller实现
//3.分页请求(原生pageHelper传参)
@RequestMapping("/dtoListPage")
public List<ModuleDto> getDtoPage(){
return moduleService.moduleDtoListPage();
}
//4.前端分页设置,后端pageInfo处理(前后端分页交互)
@RequestMapping("/pageList")
public PageDto list(PageDto pageDto){
moduleService.getModulePageDtoList(pageDto);
return pageDto;
}
- postman请求测试
http:{{source-cloudcenter}} /business/admin/module/pageList?currentPage=1&size=3
分页测试
结果pageList输出:http:
浏览器响应结果
8.前端module –》post请求
axios默认以流的方式传递给后端数据。Data{param:value}的方法,后端(接收的是表单)无法接收
axios流方式传输参数
request payload传参
9.前端分页组件:post传参:两种提交数据方式-前后端结构-流
流传参结构
<template>
<div class="pageNav" aria-label="分页">
<div class="pageChoose">
<el-button type="primary" plain :disabled="page===1" @click="selectPage(1)">首页</el-button>
<el-button type="primary" icon="el-icon-caret-left" circle
:disabled="page===1" @click="selectPage(page-1)"></el-button>
<el-button type="primary" plain v-for="p in pages" :key="p" @click="selectPage(p+1)">{{p+1}}
</el-button>
<el-button type="primary" icon="el-icon-caret-right" circle
:disabled="page===pageTotal" @click="selectPage(page+1)"></el-button>
<el-button type="primary" plain :disabled="page===pageTotal" @click="selectPage(pageTotal)">尾页</el-button>
</div>
<div class="pageSize">
<el-cascader class="sizeNum" placeholder="每页显示5行" :options="options" :show-all-levels="false"
filterable v-model="size" @change="handleChange"></el-cascader>
<el-button type="primary" plain>共{{totalSize}}条</el-button>
</div>
<div class="goPage">
<span>前往</span>
<el-input class="goPageNum" v-model="input" placeholder="1" @change="inputValue"></el-input>
<el-button v-model="input" id="goBtn" type="primary" plain @click="selectPage(input)">GO</el-button>
</div>
</div>
</template>
<script>
export default {
name: "PageHelper",
//父组件传递的参数list函数
props: {
list: {
type: Function,
default: null
},
//显示的页码数,共50页,显示5页,其他用...表示
itemCount: Number,
},
data() {
return {
message: '欢迎使用cevent分页',
/*变量名与PageDto一致,和父组件传入的参数一直,否则造成NaN等解析异常
当前页码:currentPage -->父组件page
每页条数:size
总条数(pageInfo处理的total属性为long):totalSize
查询记录放入list:listPageData
自定义总页码:pageTotal
自定义分页数组:pages
*/ page: 0,
size: 5,
totalSize: 0,
pageTotal: 0,
pages: [],
options: [{
value: 5,
label: '每页显示5行',
}, {
value: 10,
label: '每页显示10行',
}, {
value: 20,
label: '每页显示20行',
}, {
value: 50,
label: '每页显示50行',
}, {
value: 100,
label: '每页显示100行',
}],
input: null,
}
}
,
mounted() {
console.log(this.message);
}
,
methods: {
handleChange(value) {
console.log("改变的opts:",value[0]);
this.size=value[0];
},
inputValue() {
let goBtn=document.getElementById("goBtn");
console.log("输入的内容:", this.input);
},
/**
* 渲染分页组件
*/ render(page, totalSize) {
this.page = page; //当前页
this.totalSize = totalSize; //总条数 / 每页显示条数size
this.pageTotal = Math.ceil(totalSize / this.size);
//默认分页后显示的页码数:10
this.pages = this.getPageItems(this.pageTotal, page, this.itemCount || 10);
},
//查询每一页
selectPage(page) {
let _this = this;
if (page < 1) {
page = 1;
}
//如果传入的当前页>总页码
if (page > _this.pageTotal) {
page = _this.pageTotal;
}
if (this.page !== page) {
_this.page = page;
if (_this.list) {
_this.list(page);
}
}
},
//当前显示的页码
getPageItems(totalSize, current, length) {
let items = [];
if (length >= totalSize) {
for (let i = 0; i < totalSize; i++) {
//总条数放入items,遍历页码
items.push(i);
}
} else {
let base = 0;
//前移:向下取整
if (current - 0 > Math.floor((length - 1) / 2)) {
//后移:向上取整
base = Math.min(totalSize, current - 0 + Math.ceil((length - 1) / 2)) - length;
}
//条数集合
for (let i = 1; i <= length; i++) {
items.push(base + i);
}
}
return items;
},
//3.添加class
addClass(node, className) {
let reg = new RegExp("b" + className + "b");
if (!reg.test(node.className)) {
node.className += (" " + className);
}
},
//4.移除class
removeClass(node, className) {
if (node.className) {
let reg = new RegExp("b" + className + "b");
let classes = node.className;
node.className = classes.replace(reg, "");
if (/^s*$/g.test(node.className)) {
node.removeAttribute("class");
}
} else {
node.removeAttribute("class");
}
}
}
}
</script>
<style scoped>
.pageNav {
}
.pageChoose {
float: left;
margin-top: 5px;
}
.pageSize {
float: left;
margin-top: 5px;
}
.sizeNum {
margin: 0 5px;
}
.goPage {
margin: 5px 0 5px 5px;
float: left;
}
.goPageNum {
width: 40px;
margin: 0 5px;
}
/*跳转页*/</style>
10.前端module调用分页pageHelper
调用分页解析
<template>
<div class="moduleTable">
<div class="">
<el-divider class="topLine"><i class="lineIcon el-icon-document-copy"></i><span
class="lineTitle">模块列表</span></el-divider>
</div>
<div class="batchOPT">
<el-button class="batchBTN" @click="toggleSelection([modules[1],modules[2]])">批量删除</el-button>
</div>
<!--:data绑定data中预设的参数
单行显示:show-overflow-tooltip="true"
如果为tr,则需要v-for="(module,index) of modules" :key="index"
-->
<el-table
ref="multipleTable"
tooltip-effect="dark"
@selection-change="handleSelectionChange"
:data="modules"
:show-overflow-tooltip="true"
border
style="width: 100%"
class="previous-row"
:header-cell-style="{backgroundImage:'linear-gradient(#ff6e02,#ff6d00)',color:'#ffffff',}"
>
<el-table-column type="selection" width="35">
</el-table-column>
<el-table-column
sortable
fixed
prop="uniId"
label="ID"
width="60">
</el-table-column>
<el-table-column
sortable
fixed
prop="name"
label="模块名称"
width="110">
</el-table-column>
<el-table-column
sortable
:show-overflow-tooltip="true"
prop="seoTitle"
label="SEO标题"
width="110">
</el-table-column>
<el-table-column
sortable
:show-overflow-tooltip="true"
prop="seoKeyword"
label="SEO关键字"
width="120">
</el-table-column>
<el-table-column
sortable
:show-overflow-tooltip="true"
prop="seoDescription"
label="SEO描述"
width="110">
</el-table-column>
<el-table-column
sortable
prop="parentId"
label="父ID"
width="75">
</el-table-column>
<el-table-column
sortable
prop="typeId"
label="模块类型"
width="110">
</el-table-column>
<el-table-column
sortable
prop="modulePath"
label="模块路径"
width="110">
</el-table-column>
<el-table-column
sortable
prop="sort"
label="排序"
width="75">
</el-table-column>
<el-table-column
sortable
prop="iCreateTime"
label="创建时间"
width="120">
</el-table-column>
<el-table-column
sortable
prop="iUpdateTime"
label="更新时间"
width="120">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="200">
<template slot-scope="scope">
<el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button>
<el-button type="text" size="small">编辑</el-button>
<el-button type="text" size="small">删除</el-button>
<el-button type="text" size="small">排序</el-button>
</template>
</el-table-column>
</el-table>
<div class="pageHelper">
<el-button class="flushBTN" type="primary" @click="pageList(1)">刷新数据</el-button>
<!--子组件内部的方法list通过$emit发射给父组件:list="父组件定义的方法"-->
<page-helper ref="pageHelper" :list="pageList"></page-helper>
</div>
</div>
</template>
<script>
import PageHelper from "../../components/PageHelper";
export default {
name: "ModuleSet",
//引用组件
components:{PageHelper},
data() {
return {
message: '这里是大模块内容页',
modules:[]
}
},
mounted() {
console.log(this.message);
//调用admin父组件,激活父组件样式
//this.$parent.activeBar("business-module-set");
//1.普通列表查询
this.list();
//2.分页列表查询,默认初始化执行第一页
this.pageList(1);
},
methods: {
handleClick(row) {
console.log(row);
},
//多选的方法
toggleSelection(rows) {
if (rows) {
rows.forEach(row => {
this.$refs.multipleTable.toggleRowSelection(row);
});
} else {
this.$refs.multipleTable.clearSelection();
}
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
//list查询
list() {
this.$axios.get('#39;)
.then((responseData) => {
console.log("模块列表:",responseData);
//将后端返回的data绑定如return预设的参数中
//this.modules=responseData.data;
console.log("这里的modules:",this.modules);
})
},
//查询当前页,需要设置变量传入pageHelper分页插件
pageList(page){
//post传递参数一:表单方式formData
//post传递参数二:流方式,axios默认以流的方式传递给后端数据
// 前端发送流:data {param:value,param:value}
// 后端接收流:(@RequestBody 分页Dto)
this.$axios.post('#39;,{
currentPage:page, //向后端发送参数。当前页
size:this.$refs.pageHelper.size, //引用传入组件后,在标签中refs定义的别名
}).then((responseData)=>{
console.log("分页传参:",responseData.data);
/*将分页参数传入module
当前页码:currentPage
每页条数:size
总条数(pageInfo处理的total属性为long):totalSize
查询记录放入list:listPageData
*/ this.modules=responseData.data.listPageData;
//分页渲染,将当前页和总条数传入==》分页组件==》后端
this.$refs.pageHelper.render(page,responseData.data.totalSize);
})
}
}
}
</script>
<style scoped>
/*顶部line样式*/ .topLine {
background-color: #2a92ee;
}
.lineIcon {
font-size: 24px;
color: #2a92ee;
position: relative;
}
.lineTitle {
position: relative;
font-size: 22px;
color: #2a92ee;
}
/*table样式*/ .previous-row {
background: #f0f9eb;
margin-left: 2%;
max-width: 96%;
}
/*批量操作*/ .batchOPT {
float: left;
margin-left: 2%;
margin-bottom: 1%;
z-index: 1;
}
.batchBTN {
background-color: #ff5202;
color: white;
box-shadow: 1px 2px 2px #ff6e02;
}
.batchBTN:hover {
background-color: #ff3d02;
color: white;
}
.batchBTN:focus {
background-color: #ff3d02;
color: #ffffff;
}
/**分页位置/
*/ .pageHelper{
margin-left: 60px;
}
.flushBTN{
position: relative;
float: left;
max-width: 95px;
margin-right: 20px;
margin-top: 5px;
}
</style>
11.postman测试
请求:{{source-cloudcenter}} /business/admin/module/pageList
参数:
{
“currentPage” :1,
“size” :3
}
postman中json传参
content-type
body传参
12.实现列表查询,分页功能效果图
pad端
pc端
13.提交gitee
Gitee地址:
12.module表(sql添加数据),gateway添加CORS跨域配置,新增pageDto分页属性,ModuleController添加分页查询功能。前端生成pageHelper分页插件,module模块引用插件实现分页完整功能
gitee
仓库