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 | |
*/ | |
public class CorsConfig implements WebMvcConfigurer { | |
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 | |
*/ | |
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跨域配置 | |
*/ | |
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.分页请求 | |
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传参) | |
public List<ModuleDto> getDtoPage(){ | |
return moduleService.moduleDtoListPage(); | |
} | |
//4.前端分页设置,后端pageInfo处理(前后端分页交互) | |
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
仓库