商品服务 - 三级分类

1.递归查询树形结构

 @Overridepublic List<CategoryEntity> listWithTree() {//1.查出所有分类List<CategoryEntity> all = this.list();//2.组装成父子的属性结构List<CategoryEntity> level1Menus = all.stream().filter(c -> c.getParentCid().equals(0L)).map(categoryEntity -> categoryEntity.setChildren(getChildrenCategory(all,categoryEntity.getCatId())))//大于放后面 升序.sorted(Comparator.comparing(CategoryEntity::getSort)).collect(Collectors.toList());return level1Menus;}private List<CategoryEntity> getChildrenCategory(List<CategoryEntity> allList, long pCatId) {List<CategoryEntity> collect = allList.stream().filter(a -> a.getParentCid() == pCatId).sorted(Comparator.comparing(CategoryEntity::getSort)).collect(Collectors.toList());if (collect.isEmpty()) {return new ArrayList<>();} else {for (CategoryEntity categoryEntity : collect) {Long catId = categoryEntity.getCatId();List<CategoryEntity> childrenCategory = getChildrenCategory(allList, catId);categoryEntity.setChildren(childrenCategory);}return collect;}}

2.网关统一配置跨域问题

 

2.1添加 网关过滤器

package com.jmj.gulimall.gateway.config;import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.reactive.CorsWebFilter;@Configuration
public class GulimallCorsConfiguration {//网关gateway是使用webflex 进行编程的 响应式 所以用的都是reactive 包下@Beanpublic CorsWebFilter corsWebFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration corsConfiguration = new CorsConfiguration();//1.配置跨域corsConfiguration.addAllowedHeader("*");//允许哪些头跨域corsConfiguration.addAllowedMethod("*");//允许哪些请求跨域corsConfiguration.addAllowedOrigin("*");//允许哪些请求来源 跨域corsConfiguration.setAllowCredentials(true);//是否允许携带cookie进行跨域 允许 否则跨域请求将会丢失一些相关cookie信息source.registerCorsConfiguration("/**", corsConfiguration);return new CorsWebFilter(source);}}

 但是我们发现还是出现了问题

这是因为网关配置了跨域,而网关转发的微服务也配置了跨域,所以返回了两个响应头被允许,

但是浏览器只希望有一个,所以报错,这时,我们只需要去把微服务的跨域注释掉就好了。 

这是人人fast 服务的跨域配置,也是Spring MVC的 配置

/*** Copyright (c) 2016-2019 人人开源 All rights reserved.* <p>* https://www.renren.io* <p>* 版权所有,侵权必究!*/package io.renren.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("*").allowCredentials(true).allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").maxAge(3600);}
}

 

这是网关的路由,是有优先级的,从上优先级最高,如果匹配不上就会依次遍历。

如果不调换优先级,路径会被网关转发到 renrenfast 服务当中,导致没有token 返回invalid token  

spring:cloud:gateway:routes:- id: test_routeuri: http://www.baidu.compredicates:#访问的路径就是 如果是/hello?url=baidu  就转发到 https://www.baidu.com/hello?url=baidu- Query=url,baidu- id: test1_routeuri: http://www.hao123.compredicates:- Query=url,hao123- id: product_routuri: lb://gulimall-productpredicates:- Path=/api/product/**filters:- RewritePath=/api/(?<segment>.*),/$\{segment}- id: admin_routeuri: lb://renren-fastpredicates:- Path=/api/**filters:- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}##前端项目,/api

3.拖拽修改前端代码

<!--  -->
<template><div><el-switch v-model="isDraggable" active-text="开启拖拽" inactive-text="关闭拖拽"></el-switch><el-tree:data="menus":props="defaultProps"show-checkboxnode-key="catId":expand-on-click-node="false":default-expanded-keys="expandedkey"@node-expand="expend"@node-collapse="nodeClose":draggable="isDraggable":allow-drop="allowDrop"@node-drop="handleDrop"><span class="custom-tree-node" slot-scope="{ node, data }"><!-- 插槽,代替了原来的 label  里所显示的内容  将插槽内容显示在原来的每个结点上面  --><span>{{ node.label }}</span><span><el-buttonv-if="node.level <= 2"type="text"size="mini"@click="() => append(data)">Append</el-button><el-button type="text" size="mini" @click="edit(data)">Edit</el-button><el-buttonv-if="node.childNodes.length == 0"type="text"size="mini"@click="() => remove(node, data)">Delete</el-button></span></span></el-tree><el-dialog:title="submitTitle":visible.sync="dialogVisible":close-on-click-modal="false"width="30%"><el-form :model="category"><el-form-item label="分类名称"><el-input v-model="category.name" autocomplete="off"></el-input></el-form-item><el-form-item label="图标"><el-input v-model="category.icon" autocomplete="off"></el-input></el-form-item><el-form-item label="计量单位"><el-inputv-model="category.productUnit"autocomplete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="cancelForm">取 消</el-button><el-button type="primary" @click="submitForm">确 定</el-button></div></el-dialog></div>
</template><script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
class UpdateCategoryData {constructor(catId, parentCid, sort, catLevel) {this.catId = catId;this.parentCid = parentCid;this.sort = sort;this.catLevel = catLevel;}
}
export default {//import引入的组件需要注入到对象中才能使用components: {},props: {},data() {return {isDraggable: false,submitType: "",submitTitle: "",dialogVisible: false,menus: [],defaultProps: {children: "children",label: "name",},expandedkey: [],copyCategory: {},category: {catId: null,name: "",parentCid: 0,catLevel: 0,showStatus: 1,productUnit: "",icon: "",sort: 0,},};},//监听属性 类似于data概念computed: {},//监控data中的数据变化watch: {},//方法集合methods: {/**** @param {*} draggingNode 拖拽的节点* @param {*} dropNode  拖拽到的哪个节点* @param {*} dropType 拖拽类型, 前后或者 内部* @param {*} event  事件对象*/handleDrop(draggingNode, dropNode, dropType, event) {console.log(draggingNode, dropNode, dropType);//1.当前节点最新的父节点Id,let updateArray = new Array();let draggingNodeLevel = draggingNode.level;if (dropType == "inner") {let dropNodeId = dropNode.data.catId;let dropNodeLevel = dropNode.level;let childrenNew = dropNode.data.children;for (const index in childrenNew) {let { catId } = childrenNew[index];let updateCategoryData = new UpdateCategoryData(catId,dropNodeId,index,dropNodeLevel + 1);updateArray.push(updateCategoryData);}let div = dropNodeLevel + 1 - draggingNodeLevel;//递归把子节点都遍历完if (div != 0) {this.recursivelyTraverseChildNodes(draggingNode.data.children,div,updateArray);}} else {//往前插入节点或者后插入节点let parentLevel = dropNode.parent.level;console.log(parentLevel);let parentChildrenArr = {};if (parentLevel == 0) {parentChildrenArr = dropNode.parent.data;} else {parentChildrenArr = dropNode.parent.data.children;}let parentCid = dropNode.data.parentCid;for (const index in parentChildrenArr) {let { catId } = parentChildrenArr[index];let updateCategoryData = new UpdateCategoryData(catId,parentCid,index,parentLevel + 1);updateArray.push(updateCategoryData);}let div = parentLevel + 1 - draggingNodeLevel;console.log("parentLevel", parentLevel);console.log("draggingNodeLevel", draggingNodeLevel);//递归把子节点都遍历完if (div != 0) {this.recursivelyTraverseChildNodes(draggingNode.data.children,div,updateArray);}}console.log(updateArray);//发送http请求修改this.$http({url: this.$http.adornUrl("/product/category/updateList"),method: "post",data: this.$http.adornData(updateArray, false),}).then(({ data }) => {this.success("修改位置与排序成功");this.getMenus();});},recursivelyTraverseChildNodes(children, div, arr) {if (children == null || children.length == 0) {//没有子节点了return;} else {for (const child of children) {let updateCategoryData = new UpdateCategoryData(child.catId,child.parentCid,child.sort,child.catLevel + div);arr.push(updateCategoryData);this.recursivelyTraverseChildNodes(child.children, div, arr);}}},allowDrop(draggingNode, dropNode, type) {//1、被拖动的当前节点以及所在的父节点总层数不能大于3console.log("allowDrop", draggingNode, dropNode, type);let level = this.countNodeLevel(draggingNode.data,draggingNode.data.catLevel);if (type == "inner") {if (dropNode.level + level <= 3) {return true;} else {return false;}}if (type == "next") {if (dropNode.parent.level + level <= 3) {return true;} else {return false;}}if (type == "prev") {if (dropNode.parent.level + level <= 3) {return true;} else {return false;}}//1)被拖动的当前节点总层数return false;},//递归查找该节点加上子类最深层级一共有几层;包含自己算countNodeLevel(node, l) {let children = node.children;let levelMax = 0;if (children.length == 0) {return node.catLevel - l + 1;} else {for (let child of children) {let level = this.countNodeLevel(child, l);if (level > levelMax) {levelMax = level;}}}return levelMax;},resetCategory() {Object.assign(this.category, this.copyCategory);//如果你希望在 console.log 输出的时候看到对象的当前状态,//你可以在赋值操作之前进行 console.log,或者使用对象解构等方法创建一个新的对象进行输出,以确保输出的是当前状态的副本而不是对象的引用。let categoryre = {};Object.assign(categoryre, this.category);console.log("执行了重置", categoryre);},submitForm() {if (this.submitType == "add") {this.addCategory();}if (this.submitType == "edit") {this.updateCategory();}},updateCategory() {this.$http({url: this.$http.adornUrl("/product/category/update"),method: "post",data: this.$http.adornData(this.category, false),}).then(({ data }) => {this.success("修改成功");this.getMenus();this.resetCategory();this.dialogVisible = false;});},edit(data) {this.submitType = "edit";this.submitTitle = "修改分类菜单";console.log("正在修改数据", data);this.dialogVisible = true;//发送http请求获取回显数据this.$http({url: this.$http.adornUrl(`/product/category/info/${data.catId}`),method: "get",}).then(({ data }) => {let categoryEdit = data.data;console.log("回显数据", categoryEdit);this.category.catId = categoryEdit.catId;this.category.name = categoryEdit.name;this.category.parentCid = categoryEdit.parentCid;this.category.catLevel = categoryEdit.catLevel;this.category.showStatus = categoryEdit.showStatus;this.category.productUnit = categoryEdit.productUnit;this.category.icon = categoryEdit.icon;this.category.sort = categoryEdit.sort;console.log("category被回显数据", this.category);});},cancelForm() {this.resetCategory();this.dialogVisible = false;},append(data) {this.resetCategory();console.log("append", this.category);this.category.parentCid = data.catId;this.category.catLevel = data.catLevel + 1;this.category.showStatus = 1;this.category.sort = 0;this.dialogVisible = true;this.submitType = "add";this.submitTitle = "添加分类菜单";},addCategory() {console.log("提交三级分类的数据", this.category);this.$http({url: this.$http.adornUrl("/product/category/save"),method: "post",data: this.$http.adornData(this.category, false),}).then(({ data }) => {this.success("添加分类成功");this.getMenus();});this.resetCategory();this.dialogVisible = false;},remove(node, data) {console.log("remove", node, data);let ids = [data.catId];// console.log(this); //vuethis.$confirm(`是否删除【${data.name}】菜单`, "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {// console.log(this); //vuethis.$http({url: this.$http.adornUrl("/product/category/delete"),method: "post",data: this.$http.adornData(ids, false),}).then(({ data }) => {// console.log(this); //vuethis.$message({message: "菜单删除成功",type: "success",});// node.visible = false;this.getMenus();//设置需要默认展开的菜单});}).catch(() => {this.$message({message: "取消了删除",type: "warning",});});},expend(data, node, _) {console.log("展开了", node.data.catId);this.expandedkey.push(node.data.catId);},nodeClose(data, node, _) {let id = node.data.catId;console.log("收起了", id);let index = this.expandedkey.indexOf(id);this.expandedkey.splice(index, 1);},getMenus() {this.$http({url: this.$http.adornUrl("/product/category/list/tree"),method: "get",}).then(({ data }) => {console.log("成功获取到菜单数据:", data.data);this.menus = data.data;});},success(msg) {this.$message({message: msg,type: "success",});},error(msg) {this.$message({message: msg,type: "warning",});},},//生命周期 - 创建完成(可以访问当前this实例)created() {this.getMenus();Object.assign(this.copyCategory, this.category);},//生命周期 - 挂载完成(可以访问DOM元素)mounted() {},beforeCreate() {}, //生命周期 - 创建之前beforeMount() {}, //生命周期 - 挂载之前beforeUpdate() {}, //生命周期 - 更新之前updated() {}, //生命周期 - 更新之后beforeDestroy() {}, //生命周期 - 销毁之前destroyed() {}, //生命周期 - 销毁完成activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style  scoped>
</style>

4.批量删除

封装复用方法 闭包

<!--  -->
<template><div><el-switchv-model="isDraggable"active-text="开启拖拽"inactive-text="关闭拖拽"></el-switch><el-button type="danger" round @click="batchDelete">批量删除</el-button><el-tree:data="menus":props="defaultProps"show-checkboxnode-key="catId":expand-on-click-node="false":default-expanded-keys="expandedkey"@node-expand="expend"@node-collapse="nodeClose":draggable="isDraggable":allow-drop="allowDrop"@node-drop="handleDrop"ref="menuTree"><span class="custom-tree-node" slot-scope="{ node, data }"><!-- 插槽,代替了原来的 label  里所显示的内容  将插槽内容显示在原来的每个结点上面  --><span>{{ node.label }}</span><span><el-buttonv-if="node.level <= 2"type="text"size="mini"@click="() => append(data)">Append</el-button><el-button type="text" size="mini" @click="edit(data)">Edit</el-button><el-buttonv-if="node.childNodes.length == 0"type="text"size="mini"@click="() => remove(node, data)">Delete</el-button></span></span></el-tree><el-dialog:title="submitTitle":visible.sync="dialogVisible":close-on-click-modal="false"width="30%"><el-form :model="category"><el-form-item label="分类名称"><el-input v-model="category.name" autocomplete="off"></el-input></el-form-item><el-form-item label="图标"><el-input v-model="category.icon" autocomplete="off"></el-input></el-form-item><el-form-item label="计量单位"><el-inputv-model="category.productUnit"autocomplete="off"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="cancelForm">取 消</el-button><el-button type="primary" @click="submitForm">确 定</el-button></div></el-dialog></div>
</template><script>
//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
class UpdateCategoryData {constructor(catId, parentCid, sort, catLevel) {this.catId = catId;this.parentCid = parentCid;this.sort = sort;this.catLevel = catLevel;}
}
export default {//import引入的组件需要注入到对象中才能使用components: {},props: {},data() {return {isDraggable: false,submitType: "",submitTitle: "",dialogVisible: false,menus: [],defaultProps: {children: "children",label: "name",},expandedkey: [],copyCategory: {},category: {catId: null,name: "",parentCid: 0,catLevel: 0,showStatus: 1,productUnit: "",icon: "",sort: 0,},};},//监听属性 类似于data概念computed: {},//监控data中的数据变化watch: {},//方法集合methods: {confirm(msg,success,error){this.$confirm(msg, "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {success();}).catch(()=>{error();});},batchDelete() {let deleteArray = this.$refs.menuTree.getCheckedNodes();console.log("被选中的元素", deleteArray);let ids = deleteArray.map((c) => {return c.catId;});console.log("被选中的Id", ids);// this.$confirm(`是否批量删除`, "提示", {//   confirmButtonText: "确定",//   cancelButtonText: "取消",//   type: "warning",// }).then(() => {// this.$http({//   url: this.$http.adornUrl("/product/category/delete"),//   method: "post",//   data: this.$http.adornData(ids, false),// }).then(({ data }) => {//   this.success("批量删除成功");//   this.getMenus();// });// }).catch((error)=>{//   this.error("取消批量删除");// });this.confirm("是否批量删除",()=>{this.$http({url: this.$http.adornUrl("/product/category/delete"),method: "post",data: this.$http.adornData(ids, false),}).then(({ data }) => {this.success("批量删除成功");this.getMenus();});},()=>{this.error("取消批量删除");})},/**** @param {*} draggingNode 拖拽的节点* @param {*} dropNode  拖拽到的哪个节点* @param {*} dropType 拖拽类型, 前后或者 内部* @param {*} event  事件对象*/handleDrop(draggingNode, dropNode, dropType, event) {console.log(draggingNode, dropNode, dropType);//1.当前节点最新的父节点Id,let updateArray = new Array();let draggingNodeLevel = draggingNode.level;if (dropType == "inner") {let dropNodeId = dropNode.data.catId;let dropNodeLevel = dropNode.level;let childrenNew = dropNode.data.children;for (const index in childrenNew) {let { catId } = childrenNew[index];let updateCategoryData = new UpdateCategoryData(catId,dropNodeId,index,dropNodeLevel + 1);updateArray.push(updateCategoryData);}let div = dropNodeLevel + 1 - draggingNodeLevel;//递归把子节点都遍历完if (div != 0) {this.recursivelyTraverseChildNodes(draggingNode.data.children,div,updateArray);}} else {//往前插入节点或者后插入节点let parentLevel = dropNode.parent.level;console.log(parentLevel);let parentChildrenArr = {};if (parentLevel == 0) {parentChildrenArr = dropNode.parent.data;} else {parentChildrenArr = dropNode.parent.data.children;}let parentCid = dropNode.data.parentCid;for (const index in parentChildrenArr) {let { catId } = parentChildrenArr[index];let updateCategoryData = new UpdateCategoryData(catId,parentCid,index,parentLevel + 1);updateArray.push(updateCategoryData);}let div = parentLevel + 1 - draggingNodeLevel;console.log("parentLevel", parentLevel);console.log("draggingNodeLevel", draggingNodeLevel);//递归把子节点都遍历完if (div != 0) {this.recursivelyTraverseChildNodes(draggingNode.data.children,div,updateArray);}}console.log(updateArray);//发送http请求修改this.$http({url: this.$http.adornUrl("/product/category/updateList"),method: "post",data: this.$http.adornData(updateArray, false),}).then(({ data }) => {this.success("修改位置与排序成功");this.getMenus();});},recursivelyTraverseChildNodes(children, div, arr) {if (children == null || children.length == 0) {//没有子节点了return;} else {for (const child of children) {let updateCategoryData = new UpdateCategoryData(child.catId,child.parentCid,child.sort,child.catLevel + div);arr.push(updateCategoryData);this.recursivelyTraverseChildNodes(child.children, div, arr);}}},allowDrop(draggingNode, dropNode, type) {//1、被拖动的当前节点以及所在的父节点总层数不能大于3console.log("allowDrop", draggingNode, dropNode, type);let level = this.countNodeLevel(draggingNode.data,draggingNode.data.catLevel);if (type == "inner") {if (dropNode.level + level <= 3) {return true;} else {return false;}}if (type == "next") {if (dropNode.parent.level + level <= 3) {return true;} else {return false;}}if (type == "prev") {if (dropNode.parent.level + level <= 3) {return true;} else {return false;}}//1)被拖动的当前节点总层数return false;},//递归查找该节点加上子类最深层级一共有几层;包含自己算countNodeLevel(node, l) {let children = node.children;let levelMax = 0;if (children.length == 0) {return node.catLevel - l + 1;} else {for (let child of children) {let level = this.countNodeLevel(child, l);if (level > levelMax) {levelMax = level;}}}return levelMax;},resetCategory() {Object.assign(this.category, this.copyCategory);//如果你希望在 console.log 输出的时候看到对象的当前状态,//你可以在赋值操作之前进行 console.log,或者使用对象解构等方法创建一个新的对象进行输出,以确保输出的是当前状态的副本而不是对象的引用。let categoryre = {};Object.assign(categoryre, this.category);console.log("执行了重置", categoryre);},submitForm() {if (this.submitType == "add") {this.addCategory();}if (this.submitType == "edit") {this.updateCategory();}},updateCategory() {this.$http({url: this.$http.adornUrl("/product/category/update"),method: "post",data: this.$http.adornData(this.category, false),}).then(({ data }) => {this.success("修改成功");this.getMenus();this.resetCategory();this.dialogVisible = false;});},edit(data) {this.submitType = "edit";this.submitTitle = "修改分类菜单";console.log("正在修改数据", data);this.dialogVisible = true;//发送http请求获取回显数据this.$http({url: this.$http.adornUrl(`/product/category/info/${data.catId}`),method: "get",}).then(({ data }) => {let categoryEdit = data.data;console.log("回显数据", categoryEdit);this.category.catId = categoryEdit.catId;this.category.name = categoryEdit.name;this.category.parentCid = categoryEdit.parentCid;this.category.catLevel = categoryEdit.catLevel;this.category.showStatus = categoryEdit.showStatus;this.category.productUnit = categoryEdit.productUnit;this.category.icon = categoryEdit.icon;this.category.sort = categoryEdit.sort;console.log("category被回显数据", this.category);});},cancelForm() {this.resetCategory();this.dialogVisible = false;},append(data) {this.resetCategory();console.log("append", this.category);this.category.parentCid = data.catId;this.category.catLevel = data.catLevel + 1;this.category.showStatus = 1;this.category.sort = 0;this.dialogVisible = true;this.submitType = "add";this.submitTitle = "添加分类菜单";},addCategory() {console.log("提交三级分类的数据", this.category);this.$http({url: this.$http.adornUrl("/product/category/save"),method: "post",data: this.$http.adornData(this.category, false),}).then(({ data }) => {this.success("添加分类成功");this.getMenus();});this.resetCategory();this.dialogVisible = false;},remove(node, data) {console.log("remove", node, data);let ids = [data.catId];// console.log(this); //vuethis.$confirm(`是否删除【${data.name}】菜单`, "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {// console.log(this); //vuethis.$http({url: this.$http.adornUrl("/product/category/delete"),method: "post",data: this.$http.adornData(ids, false),}).then(({ data }) => {// console.log(this); //vuethis.$message({message: "菜单删除成功",type: "success",});// node.visible = false;this.getMenus();//设置需要默认展开的菜单});}).catch(() => {this.$message({message: "取消了删除",type: "warning",});});},expend(data, node, _) {console.log("展开了", node.data.catId);this.expandedkey.push(node.data.catId);},nodeClose(data, node, _) {let id = node.data.catId;console.log("收起了", id);let index = this.expandedkey.indexOf(id);this.expandedkey.splice(index, 1);},getMenus() {this.$http({url: this.$http.adornUrl("/product/category/list/tree"),method: "get",}).then(({ data }) => {console.log("成功获取到菜单数据:", data.data);this.menus = data.data;});},success(msg) {this.$message({message: msg,type: "success",});},error(msg) {this.$message({message: msg,type: "warning",});},},//生命周期 - 创建完成(可以访问当前this实例)created() {this.getMenus();Object.assign(this.copyCategory, this.category);},//生命周期 - 挂载完成(可以访问DOM元素)mounted() {},beforeCreate() {}, //生命周期 - 创建之前beforeMount() {}, //生命周期 - 挂载之前beforeUpdate() {}, //生命周期 - 更新之前updated() {}, //生命周期 - 更新之后beforeDestroy() {}, //生命周期 - 销毁之前destroyed() {}, //生命周期 - 销毁完成activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style  scoped>
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/293870.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode-560. 和为 K 的子数组【数组 哈希表 前缀和】

LeetCode-560. 和为 K 的子数组【数组 哈希表 前缀和】 题目描述&#xff1a;解题思路一&#xff1a;一边算前缀和一边统计。这里用哈希表统计前缀和出现的次数&#xff0c;那么和为k的子数组的个数就是当前前缀和-k的个数&#xff0c;即preSums[presum - k]。画个图表述就是&a…

FPGA之状态机学习

作为一名逻辑工程师&#xff0c;掌握和应用状态机设计是必不可少的。能够灵活的应用状态机是对逻辑工程师最基本的要求&#xff0c;状态机设计的好坏能够直接影响到设计系统的稳定性&#xff0c;所以学会状态机是非常的重要。 1.状态机的概念 状态机通过不同的状态迁移来完成特…

Java封装最佳实践:打造高内聚、低耦合的优雅代码~

​ 个人主页&#xff1a;秋风起&#xff0c;再归来~ 文章专栏&#xff1a;javaSE的修炼之路 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c;律己则安&#xff01; 1、封装 1.1 封装的概念 面向对象程序三大…

从0到1利用express搭建后端服务

目录 1 架构的选择2 环境搭建3 安装express4 创建启动文件5 express的核心功能6 加入日志记录功能7 日志记录的好处本节代码总结 不知不觉学习低代码已经进入第四个年头了&#xff0c;既然低代码很好&#xff0c;为什么突然又自己架构起后端了呢&#xff1f;我有一句话叫低代码…

【数据结构】优先级队列——堆

&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;个人主页&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388; &#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;数据结构专栏&#x1f388;&#x1f388;&#x1f388;&…

数码管时钟--LABVIEW编程

一、程序的前面板 1.获取系统时钟&#xff0c;年月日&#xff0c;时分秒&#xff0c;用14个数码管显示。 2.闹钟设定小时和分钟。 二、程序的后面板 三、程序运行图 四、程序源码 源程序可以在百度网盘自行下载&#xff0c;地址链接见下方。 链接&#xff1a;https://pan.b…

开源,微信小程序-超级计算器T3000 简介

笔者于四年前自学微信小程序开发&#xff0c;这个超级计算器T3000就是当时的练习作品。超级计算器T3000的功能有很多&#xff0c;其中的核心技术是矩阵计算&#xff0c;使用的工具库是math.js&#xff0c;其次是复杂运算和分式运算。关于math.js的使用&#xff0c;可以参考另一…

如何在极狐GitLab 配置 邮件功能

本文作者&#xff1a;徐晓伟 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 本文主要讲述了在极狐GitLab 用户…

图片标注编辑平台搭建系列教程(4)——fabric几何定制渲染

背景 标注的几何&#xff0c;有时需要一些定制化的渲染样式&#xff0c;例如&#xff0c;线中间展示箭头&#xff0c;表示方向。本期教程教大家如何实现fabric几何定制化渲染。 带箭头的线 fabric提供了一些原生的几何&#xff0c;例如Point、Polyline、Polygon。同时提供了…

LangChain入门:2.OpenAPI调用ChatGPT模型

引言 在本文中&#xff0c;我们将带您深入探索如何通过OpenAPI与ChatGPT模型进行高效交互&#xff0c;实现智能文本问答功能。通过LangChain库的实践&#xff0c;您将学习构建一个能够与用户进行自然语言对话的系统的关键步骤。 准备步骤 在动手编码之前&#xff0c;请确保您…

Collection与数据结构链表与LinkedList(三):链表精选OJ例题(下)

1. 分割链表 OJ链接 class Solution {public ListNode partition(ListNode head, int x) {if(head null){return null;//空链表的情况}ListNode cur head;ListNode formerhead null;ListNode formerend null;ListNode latterhead null;ListNode latterend null;//定义…

Beans模块之工厂模块DisposableBean

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

宝塔面板操作一个服务器域名部署多个网站

此处记录IP一样&#xff0c;端口不一样的操作方式&#xff1a; 宝塔面板操作&#xff1a; 1、创建第一个网站&#xff1a; 网站名用IP地址&#xff0c;默认80端口。 创建好后&#xff0c;直接IP访问就可以了。看到自带的默认首页 2、接下来部署第二个网站&#xff1a; 仍然是…

docker-compose mysql

使用docker-compose 部署 MySQL&#xff08;所有版本通用&#xff09; 一、拉取MySQL镜像 我这里使用的是MySQL8.0.18&#xff0c;可以自行选择需要的版本。 docker pull mysql:8.0.18二、创建挂载目录 mkdir -p /data/mysql8/log mkdir -p /data/mysql8/data mkdir -p /dat…

IC-随便记

1、移远通信---通信模组 物联网解决方案供应商&#xff0c;可提供完备的IoT产品和服务&#xff0c;涵盖蜂窝模组(5G/4G/3G/2G/LPWA)、车载前装模组、智能模组&#xff08;5G/4G/边缘计算&#xff09;、短距离通信模组(Wi-Fi&BT)、GNSS定位模组、卫星通信模组、天线等硬件产…

阿里云CentOS7安装MySQL8

创建目录 [rootnode1 ~]# mkdir /usr/local/mysql [rootnode1 ~]# cd /usr/local/mysql/ 下载安装包 到MySQL官网查看需要下载的版本&#xff0c;并获取到下载地址 https://downloads.mysql.com/archives/community/下载 [rootnode1 mysql]# wget https://downloads.mysql…

Linux - 第三节

改变用户类型 su 仅单纯的进行身份变化 依旧处于普通用户里面 su - 进行重新登录更改身份 退出用exit / ctrld su 用户名 改成成其他身份 对一条命令进行提权 sudo command r:可读 w:可写 x:可执行 -:对应的权限位置&#xff0c;没有权限 去掉所有权限 chmod u…

java日志技术——Logback日志框架安装及概述

前言&#xff1a; 整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup!!! 日志 什么是日志 程序中的日志&#xff0c;通常就是一个文件&#xff0c;里面记录的是程序运行过程中的各种信息&#xff0c;通过日志可以进行操作分析&#xff0c;bug定位等 记录日志的方案 程…

嵌入式系统基础知识(一):嵌入式系统是什么?

一.定义 根据IEEE&#xff08;国际电气和电子工程师协会&#xff09;的定义&#xff0c;嵌入式系统是“控制、监视或者辅助设备、机器和车间运行的装置”。这主要是从应用上加以定义的&#xff0c;从中可看出嵌入式系统是软件和硬件的综合体&#xff0c;还可以涵盖机械等附属装…

云渲染实用工具:3ds max怎么改低版本?

3ds Max是建模领域广泛采用的专业软件&#xff0c;它通过定期更新来不断增强功能和提升性能。但这些频繁的更新有时会导致一些插件暂时无法与新版本完全兼容。为了解决这个问题&#xff0c;设计师们可以采用一个简单有效的方法&#xff0c;那就是将较新版本的3ds Max文件进行版…