19_微信小程序之优雅实现侧滑菜单

news2024/9/22 13:32:19

19_微信小程序之优雅实现侧滑菜单

一.先上效果图

在这里插入图片描述

要实现这样一个效果,布局其实很简单,整体布局是一个横向滚动的scroll-view,难点在于怎么控制侧滑菜单的回弹,以及寻找回弹的边界条件? 此篇文章主要是基于uni-app来实现的,以后也将继续使用uni-app,但是即使使用的是原生微信小程序框架也不影响,思路都是一样的,而且uni-app的api和原生微信小程序api是对标的。

二.整体布局实现

整体布局是一个横向滚动的scroll-view,scroll-view内部有两个标签,第一个标签是内容区域,宽度占满组件的宽度,高度自适应,第二个标签用于摆放侧滑按钮,宽度为每一个侧滑按钮的宽度之和,由于是自定义组件,所以预留了slot插槽。

<template>
	<scroll-view class="swipe-cell__inner" enable-flex scroll-x>
		<view class="swipe-cell__content">
			<slot></slot>
		</view>
		<view class="swipe-cell__right">
			<slot name="right"></slot>
		</view>
	</scroll-view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				
			};
		}
	}
</script>

<style scoped>
	.swipe-cell__inner /deep/ ::-webkit-scrollbar {
		display: block;
		width: 0px !important;
		height: 0px !important;
	}
	
	.swipe-cell__inner {
		width: 100%;
		display: inline-flex;
		flex-direction: row;
		align-items: flex-start;
		white-space: nowrap;
	}
	
	.swipe-cell__content {
		display: inline-block;
		width: 100%;
		flex-shrink: 0;
		position: relative;
		white-space: normal;
		overflow: hidden;
	}
	
	.swipe-cell__right {
		align-self: stretch;
		display: inline-flex;
		flex-direction: row;
		align-items: stretch;
		position: relative;
		white-space: normal;
	}
</style>

三.在页面中使用该组件

我们先把组件引入使用,页面布局没问题之后,再来考虑侧滑面板的回弹效果。

<template>
	<scroll-view class="scroll-view" scroll-y>
		<swipe-cell class="swipe-cell" v-for="(item, index) in 3">
			<view class="user-item">
				<image class="user-avatar" src="/static/avatar.png"/>
				<view class="user-info-group">
					<view class="user-name">andr_gale</view>
					<view class="user-desc clamp1">不管做什么事,必定有人赞成有人反对,因为大家重视的东西都有所不同。而且,不管什么事情,都可以随意给他加上好与不好的理由,所以,若果一定要分清争议与罪恶的行为,反而有问题。因此,重要的事情由心去决定就行了,不是凭感情,而是凭心</view>
				</view>
			</view>
			
			<template #right>
				<view class="user-button-group">
					<view class="user-button-follow">关注</view>
					<view class="user-button-chat">私信</view>
				</view>
			</template>
		</swipe-cell>
	</scroll-view>
</template>

<script>
	export default {
		data() {
			return {
				
			}
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>
	page {
		width: 100%;
		height: 100%;
		overflow: hidden;
		display: flex;
		flex-direction: column;
		align-items: stretch;
	}
	
	.clamp1 {
		display: -webkit-box;
		word-break: break-all;
		overflow: hidden;
		-webkit-box-orient: vertical;
		text-overflow: ellipsis;
		-webkit-line-clamp: 1;
	}
	
	.clamp2 {
		display: -webkit-box;
		word-break: break-all;
		overflow: hidden;
		-webkit-box-orient: vertical;
		text-overflow: ellipsis;
		-webkit-line-clamp: 2;
	}
	
	.clamp3 {
		display: -webkit-box;
		word-break: break-all;
		overflow: hidden;
		-webkit-box-orient: vertical;
		text-overflow: ellipsis;
		-webkit-line-clamp: 3;
	}
	
	.scroll-view {
		height: 100%;
	}
	
	.swipe-cell {
		display: block;
		border-bottom: thin solid #f2f2f2;
	}
	
	.swipe-cell:last-child {
		border-bottom: none;
	}
	
	.user-item {
		display: flex;
		flex-direction: row;
		align-items: stretch;
		padding: 20rpx;
		box-sizing: border-box;
	}
	
	.user-avatar {
		width: 100rpx;
		height: 100rpx;
		display: block;
		border-radius: 50rpx;
		box-sizing: border-box;
		overflow: hidden;
	}
	
	.user-info-group {
		flex: 1;
		overflow: hidden;
		margin-left: 20rpx;
		display: flex;
		flex-direction: column;
		align-items: stretch;
		justify-content: center;
	}
	
	.user-name {
		font-size: 32rpx;
		color: #000;
		line-height: 1;
		margin-bottom: auto;
	}
	
	.user-desc {
		font-size: 24rpx;
		color: #999;
		line-height: 1;
		margin-top: auto;
	}
	
	.user-button-group {
		display: flex;
		flex-direction: row;
		align-items: stretch;
	}
	
	.user-button-follow {
		width: 160rpx;
		background: orange;
		font-size: 28rpx;
		color: white;
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: center;
	}
	
	.user-button-chat {
		width: 160rpx;
		background: #09BB07;
		font-size: 28rpx;
		color: white;
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: center;
	}
</style>

在这里插入图片描述

四.什么时候向左回弹打开? 什么时候向右回弹关闭

在这里插入图片描述

上图中,红色区域是一条辅助线,被固定在了组件的最由边的位置,为了好演示,我把宽度设置的比较宽,正常1px就够了,蓝色区域为侧滑面板的右半边。

当我们滑动完,放手后,红色区域与蓝色区域相交,这时应该向左回弹打开。

当我们滑动完,放手后,红色区域与蓝色区域不相交,这时应该向右回弹关闭。

<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x>
			<view class="swipe-cell__content">
				<slot></slot>
			</view>
			<view class="swipe-cell__right">
				<view class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	...
</script>

<style scoped>
	.swipe-cell__container {
		position: relative;
		width: 100%;
	}
	
	...
	
	.swipe-cell__right-half {
		position: absolute;
		left: 50%;
		top: 0;
		width: 50%;
		height: 100%;
		background: blue;
	}
	
	.swipe-cell__guide-line {
		position: absolute;
		width: 6px;
		top: 0;
		bottom: 0;
		right: 0;
		background: red;
	}
</style>

问题转化为怎么判断红色区域与蓝色区域相交,我们的主角正式登场,使用IntersectionObserver可监听两个或多个组件节点在布局位置上的相交状态。

  • IntersectionObserver wx.createIntersectionObserver(Object component, Object options)

    • 在页面中使用: IntersectionObserver wx.createIntersectionObserver(this, {observeAll: true}),observeAll为true可同时监听多个节点
    • 在自定义组件中使用:IntersectionObserver this.createIntersectionObserver({observeAll: true})
  • IntersectionObserver IntersectionObserver.relativeTo(string selector, Object margins)

    • 使用选择器指定一个节点,作为参照区域
  • IntersectionObserver.observe(string targetSelector, IntersectionObserver.observeCallback callback)

    • 指定目标节点的选择器并监听由relativeTo指定的参照区域与目标节点是否相交,由于我们需要监听多个video节点,所以这里的目标节点选择器我们使用class选择器即可。
    • 当目标节点与参照区域相交时,callback(res)返回的res中的intersectionRatio大于0
    • 当目标节点与参照区域不相交时,callback(res)返回的res中的intersectionRatio等于0
  • IntersectionObserver.disconnect()

    • 最后当页面或组件销毁的时候,需调用IntersectionObserver.disconnect()取消监听
<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x @touchend="onTouchEnd($event)">
			<view class="swipe-cell__content">
				<slot></slot>
			</view>
			<view class="swipe-cell__right">
				<view id="observable" class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view id="guide-line" class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				intersectionObserver: undefined,
				intersectionRatio: -1,
			};
		},
		mounted() {
			this.intersectionObserver = uni.createIntersectionObserver(this)
			this.intersectionObserver.relativeTo("#guide-line")
				.observe("#observable", (res) => {
					let intersectionRatio = res.intersectionRatio
					this.intersectionRatio = intersectionRatio
				})
		},
		unmounted() {
			this.intersectionObserver.disconnect()
		},
		methods: {
			onTouchEnd: function(event) {
				if(this.intersectionRatio > 0) {
					//红色区域与蓝色区域相交,向左回弹打开
					console.log("onTouchEnd", "向左回弹打开")
				} else {
					//红色区域与蓝色区域不相交,向右回弹关闭
					console.log("onTouchEnd", "向右回弹关闭")
				}
			}
		}
	}
</script>

<style scoped>
  ...
</style>

五.怎么控制回弹?

通过设置scroll-view的scroll-into-view属性的值,滚动到指定元素来控制回弹,并设置scroll-with-animation为true来开启自动滚动动画效果,我们给内容区域的标签指定id为content,给侧滑面板的标签指定id为right,那么:

当红色区域与蓝色区域相交时,设置scroll-into-view属性的值为right,向左回弹打开

当红色区域与蓝色区域不相交,设置scroll-into-view属性的值为content,向左回弹关闭

<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x @touchend="onTouchEnd($event)" :scroll-into-view="scrollIntoView" scroll-with-animation>
			<view id="content" class="swipe-cell__content">
				<slot></slot>
			</view>
			<view id="right" class="swipe-cell__right">
				<view id="observable" class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view id="guide-line" class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				intersectionObserver: undefined,
				intersectionRatio: -1,
				scrollIntoView: "content"
			};
		},
		...
		methods: {
			onTouchEnd: function(event) {
				if(this.intersectionRatio > 0) {
					//红色区域与蓝色区域相交,向左回弹打开
					console.log("onTouchEnd", "向左回弹打开")
					this.scrollIntoView = "right"
				} else {
					//红色区域与蓝色区域不相交,向右回弹关闭
					console.log("onTouchEnd", "向右回弹关闭")
					this.scrollIntoView = "content"
				}
			}
		}
	}
</script>

<style scoped>
  ...
</style>

在这里插入图片描述

仔细观察上图,我们回发现有一个问题,当我们两次放手,两次的相交状态从不相交到相交或者从相交到不相交时,能正常回弹,

从相交到相交或者从不相交到不相交时,则不会回弹,这是因为:

从相交到相交的过程中,scroll-into-view始终为right不变

从不相交到不相交的过程中,scroll-into-view始终为content不变

要解决这个问题,我们需要在每一次放手的时候,产生一个唯一的随机数,给内容区域的标签指定id为content- 随机数,给侧滑面板的标签指定 i d 为 r i g h t − {随机数},给侧滑面板的标签指定id为right- 随机数,给侧滑面板的标签指定idright{随机数},最后,如果是回弹打开,设置scroll-into-view的值为right- 随机数,如果是回弹关闭,设置 s c r o l l − i n t o − v i e w 的值为 c o n t e n t − {随机数},如果是回弹关闭,设置scroll-into-view的值为content- 随机数,如果是回弹关闭,设置scrollintoview的值为content{随机数}。

这个随机数的生成规则,你可以自己写算法实现,这里我用了一种比较巧妙的办法,那就是在事件触发的回调中,系统会给我们一个event对象,我们通过这个event对象的timeStamp属性,可以获取到事件触发的时间戳,这个时间戳必然是唯一的,因此event.timesSamp就可以作为这个唯一的随机数来使用。

<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x @touchend="onTouchEnd($event)" :scroll-into-view="scrollIntoView" scroll-with-animation>
			<view :id="'content-' + random" class="swipe-cell__content">
				<slot></slot>
			</view>
			<view :id="'right-' + random" class="swipe-cell__right">
				<view id="observable" class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view id="guide-line" class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				intersectionObserver: undefined,
				intersectionRatio: -1,
				scrollIntoView: "content-0",
				random: 0,
			};
		},
		...
		methods: {
			onTouchEnd: function(event) {
				this.random = event.timeStamp || 0
				if(this.intersectionRatio > 0) {
					//红色区域与蓝色区域相交,向左回弹打开
					console.log("onTouchEnd", "向左回弹打开")
					this.$nextTick(() => {
						this.scrollIntoView = "right-" + this.random
					})
				} else {
					//红色区域与蓝色区域不相交,向右回弹关闭
					console.log("onTouchEnd", "向右回弹关闭")
					this.$nextTick(() => {
						this.scrollIntoView = "content-" + this.random
					})
				}
			}
		}
	}
</script>

<style scoped>
  ...
</style>

在这里插入图片描述

最后,我们把辅助色块去掉,就大功告成了。

<style scoped>
  ...
  
  .swipe-cell__right-half {
		position: absolute;
		left: 50%;
		top: 0;
		width: 50%;
		height: 100%;
		z-index: -1;
	}
	
	.swipe-cell__guide-line {
		position: absolute;
		width: 1px;
		top: 0;
		bottom: 0;
		right: 0;
		z-index: -1;
	}
</style>

在这里插入图片描述

六.完整代码

<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x @touchend="onTouchEnd($event)" :scroll-into-view="scrollIntoView" scroll-with-animation>
			<view :id="'content-' + random" class="swipe-cell__content">
				<slot></slot>
			</view>
			<view :id="'right-' + random" class="swipe-cell__right">
				<view id="observable" class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view id="guide-line" class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				intersectionObserver: undefined,
				intersectionRatio: -1,
				scrollIntoView: "content-0",
				random: 0,
			};
		},
		mounted() {
			this.intersectionObserver = uni.createIntersectionObserver(this)
			this.intersectionObserver.relativeTo("#guide-line")
				.observe("#observable", (res) => {
					let intersectionRatio = res.intersectionRatio
					this.intersectionRatio = intersectionRatio
				})
		},
		unmounted() {
			this.intersectionObserver.disconnect()
		},
		methods: {
			onTouchEnd: function(event) {
				this.random = event.timeStamp || 0
				if(this.intersectionRatio > 0) {
					//红色区域与蓝色区域相交,向左回弹打开
					console.log("onTouchEnd", "向左回弹打开")
					this.$nextTick(() => {
						this.scrollIntoView = "right-" + this.random
					})
				} else {
					//红色区域与蓝色区域不相交,向右回弹关闭
					console.log("onTouchEnd", "向右回弹关闭")
					this.$nextTick(() => {
						this.scrollIntoView = "content-" + this.random
					})
				}
			}
		}
	}
</script>

<style scoped>
	.swipe-cell__container {
		position: relative;
		width: 100%;
	}
	
	.swipe-cell__inner /deep/ ::-webkit-scrollbar {
		display: block;
		width: 0px !important;
		height: 0px !important;
	}
	
	.swipe-cell__inner {
		width: 100%;
		display: inline-flex;
		flex-direction: row;
		align-items: flex-start;
		white-space: nowrap;
	}
	
	.swipe-cell__content {
		display: inline-block;
		width: 100%;
		flex-shrink: 0;
		position: relative;
		white-space: normal;
		overflow: hidden;
	}
	
	.swipe-cell__right {
		align-self: stretch;
		display: inline-flex;
		flex-direction: row;
		align-items: stretch;
		position: relative;
		white-space: normal;
	}
	
	.swipe-cell__right-half {
		position: absolute;
		left: 50%;
		top: 0;
		width: 50%;
		height: 100%;
		z-index: -1;
	}
	
	.swipe-cell__guide-line {
		position: absolute;
		width: 1px;
		top: 0;
		bottom: 0;
		right: 0;
		z-index: -1;
	}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL 记录锁+间隙锁可以防止删除操作而导致的幻读吗?

文章目录什么是幻读&#xff1f;实验验证加锁分析总结什么是幻读&#xff1f; 首先来看看 MySQL 文档是怎么定义幻读&#xff08;Phantom Read&#xff09;的: The so-called phantom problem occurs within a transaction when the same query produces different sets of r…

使用Fairseq进行Bart预训练

文章目录前言环境流程介绍数据部分分词部分预处理部分训练部分遇到的问题问题1可能遇到的问题问题1问题2前言 本文是使用 fairseq 做 Bart 预训练任务的踩坑记录huggingface没有提供 Bart 预训练的代码 facebookresearch/fairseq: Facebook AI Research Sequence-to-Sequence…

字符串匹配 - 文本预处理:后缀树(Suffix Tree)

上述字符串匹配算法(朴素的字符串匹配算法, KMP 算法, Boyer-Moore算法)均是通过对模式&#xff08;Pattern&#xff09;字符串进行预处理的方式来加快搜索速度。对 Pattern 进行预处理的最优复杂度为 O(m)&#xff0c;其中 m 为 Pattern 字符串的长度。那么&#xff0c;有没有…

windows环境下,vue启动项目后打开chrome浏览器

前言&#xff1a;关于vue启动后打开chrome浏览器&#xff0c;我查了很多资料&#xff0c;方案如下&#xff1a; 1、增加环境变量BROWSER为chrome&#xff08;试了没效果&#xff09; 2、设置系统的默认浏览器为chrome&#xff08;应该可以&#xff0c;但没试&#xff1b;因为…

有序表的应用:[Leetcode 327] 区间和的个数

一、题目 1、题目描述 给你一个整数数组 nums 以及两个整数 lower 和 upper 。求数组中&#xff0c;值位于范围 [lower, upper] &#xff08;包含 lower 和 upper&#xff09;之内的 区间和的个数 。 区间和 S(i, j)表示在 nums 中&#xff0c;位置从 i 到 j 的元素之和&…

基于jsplumb构建的流程设计器

项目背景 最近在准备开发工作流引擎相关模块&#xff0c;完成表结构设计后开始着手流程设计器的技术选型&#xff0c;调研了众多开源项目后决定基于jsplumb.js开源库进行自研开发&#xff0c;保证定制化的便捷性&#xff0c;相关效果图及项目地址如下 项目地址&#xff1a;ht…

进程切换-

实验课之前有一些问题 中断机制 第一个问题&#xff1a; interrupt的两个状态源头&#xff1a; 外中断多由随机中断&#xff08;异步中断&#xff09;造成&#xff0c;如鼠标点击&#xff0c;键盘输入&#xff1b; 内终端多由故障终端&#xff1a;程序运行异常&#xff0c;硬件…

使用PyQtGraph 自定义绘图

Python 的主要优势之一是探索性数据科学和可视化生态体系。一般的工具链为Pandas、numpy、sklearn 进行数据分析和使用matplotlib进行绘图。 但是如果需要自己自定义一个个性化的图形界面工具&#xff0c;则可能不是很合适。为了实现这种需求&#xff0c;可以使用PyQt构建GUI应…

【进阶篇】线程的硬件基础

文章目录高速缓存缓存一致性协议写缓冲区和无效化队列高速缓存 简介 高速缓存是主内存与处理器之间的硬件&#xff0c;其容量小于主存&#xff0c;但存取速率远高于主存。因此处理器在执行读写操作时&#xff0c;可直接和高速缓存交互&#xff0c;提高响应速度。 我们常见的变…

2049. 统计最高分的节点数目

2049. 统计最高分的节点数目题目算法设计&#xff1a;深度优先搜索题目 传送门&#xff1a;https://leetcode.cn/problems/count-nodes-with-the-highest-score/ 算法设计&#xff1a;深度优先搜索 这题的核心是计算分数。 一个节点的分数 左子树节点数 右子树节点数 除自…

【Opencv 系列】 第4章 直方图

文章目录[TOC](文章目录)前言1、直方图的定义、意义、特征2、直方图&#xff1a;2.1 灰度直方图2.2 彩色直方图前言 1、直方图的定义、意义、特征 在统计学中&#xff0c;直方图是一种对数据分布情况的图形表示&#xff0c;是一种二维统计图表&#xff0c;他的两个坐标分别是统…

docker安装配置镜像加速器-拉取创建Mysql容器示例

List item docker 常见命令大全docker安装docker拉取创建Mysql容器docker 安装 1、安装链接&#xff1a;https://blog.csdn.net/BThinker/article/details/123358697 &#xff1b; 2、安装完成需要配置docker镜像加速器 3、docker 镜像加速器推荐使用阿里云的&#xff1a; 编…

硬件学习 软件Cadence day04 PCB 封装绘制

1.文章内容&#xff1a; 1. 贴片式电容 PCB 封装绘制 &#xff08;型号 c0603 &#xff09; 2. 贴片式电阻 PCB 封装绘制 &#xff08;型号 r0603 &#xff09; 3. 安规式电容 PCB 封装绘制 &#xff08;这个就是 有一个电容&#xff0c;插入一个搞好的孔里面 …

社区宠物诊所管理系统

目录第一章概述 PAGEREF _Toc4474 \h 21.1引言 PAGEREF _Toc29664 \h 31.2开发背景 PAGEREF _Toc3873 \h 3第二章系统总体结构及开发 PAGEREF _Toc19895 \h 32.1系统的总体设计 PAGEREF _Toc6615 \h 32.2开发运行环境 PAGEREF _Toc13054 \h 3第三章数据库设计 PAGEREF _Toc2852…

Prometheus 告警机制介绍及命令解读

本文您将了解到Prometheus 告警模块Alertmanager的架构介绍、核心概念、命令解析和AMTool的基本使用。 Prometheus的告警模块并不存在于Prometheus中,而是 以独立项目Alertmanager存在。Prometheus服务器定义告警规则,这些规则将触发警报,将警报发送到Alertmanager。Alertma…

DaVinci 偏好设置:系统 - 解码选项

偏好设置 - 系统/解码选项Preferences - System/Decode Options解码选项Decode Options使用 GPU 进行 Blackmagic RAW 解码Use GPU for Blackmagic RAW decode允许使用 GPU 来加速 Blackmagic RAW&#xff08;BRAW&#xff09;媒体的解码。使用硬件加速解码 H.264/H.265Decode …

谁再用Double定义存储计算金额,我劈了他

不是三婶儿偏执&#xff0c;非要吐槽。家人们&#xff0c;咱就是说&#xff0c;按照基操逻辑谁会把严格金额计算相关的数据使用double类型呢… “我以为吕布已经够勇猛了&#xff0c;这是谁的部下&#xff1f;” 前几天&#xff0c;一同事让帮忙写段代码。内容比较常规&#xf…

Unity之ASE实现边缘光效果

一.前言 今天来实现一个简单的边缘光效果&#xff0c;可以应用与物体表面的一种荧光外溢的效果&#xff0c;特别是用在人的身体表面&#xff0c;会让人的皮肤更细腻&#xff0c;更好看。 物体上效果如下: 人体表面效果如下&#xff1a; 我们可以看到&#xff0c;这种人体表面…

Redis的整合和使用

引入依赖 <!--整合redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>添加配置 spring.redis.host127.0.0.1 spring.redis.port6379 spring.…

算法训练营 day49 动态规划 爬楼梯 (进阶)零钱兑换 完全平方数

算法训练营 day49 动态规划 爬楼梯 &#xff08;进阶&#xff09;零钱兑换 完全平方数 爬楼梯 &#xff08;进阶&#xff09; 70. 爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同…