最近在做一个自己的全栈项目,前端用的uni-app,主要也是想巩固一下uni-app这方面的知识,其中有一个页面,涉及到ai聊天的功能,我理想型是类似文心一言或者微信聊天页面的效果,但是研究半天,发现uni-app的scroll-view这个内置组件并不能像写pc的滚动条那么灵活,因为在小程序或者app没有dom这个概念,网上找了一堆资源,基本上全是vue2版本的,密密麻麻的文字,真是看不下去,自己动手,丰衣足食。

一、代码部分:
<template>
	<view class="content">
		<view class="container">
			<!--简介: scroll-with-animation:动画,开启有一个过渡效果;scroll-into-view:向指定id滚动 -->
			<scroll-view @scrolltoupper="addInfo" scroll-with-animation :scroll-into-view="item" class="Scroll" scroll-y="true">
				<view class="box" v-for="(item,index) in dialogList" :key="index" :id="`item-${index}`">
					<view class="input">
						<p class="i">{{ item.input }}</p>
					</view>
					<view class="output">
						<p class="o">{{ item.output }}</p>
					</view>
				</view>
			</scroll-view>
		</view>
		<view class="footer">
			<view class="ipt">
				<input placeholder="请输入内容" class="IPT" v-model="sendInfo"/>
			</view>
			<view class="btn">
				<button class="BTN" type="primary" @click="send">发送</button>
			</view>
		</view>
	</view>
</template>

<script setup>
import { ref,watch,nextTick } from 'vue';

// scroll-into-view指向的id值
let item = ref('')

// 发送的内容
let sendInfo = ref("")

//模拟的虚拟数据 
const dialogList = ref([
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"首先,世界上菜品的总数是一个难以准确估量的数字。不同的地区、文化、民族、厨师等都会创造出新的菜品,同时传统的菜品也在不断地发展和变化。因此,即使尝试估算,这个数字也会是一个非常大的数。其次,一个人一生的天数也是有限的。假设一个人活到80岁,那么一生大约有29,200天(不考虑闰年)。这个数字与世界上菜品的总数相比,显然是微不足道的。"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	},
	{
		input:"你好",
		output:"你好,很高兴认识你!"
	}
])

// 动态更新item的值
watch(dialogList,(newval,oldval)=>{
	// 重新赋值item,延迟到dom更新之后进行,否则没效果
	nextTick(()=>{
		item.value = "item-" + (newval.length - 1)
	})
},{
	deep:true, //深度监视
	immediate:true //初始化立即执行
})

// 发送
const send = () => {
	let obj = {
		input:sendInfo.value,
		output:"......"
	}
	// 追加数据
	dialogList.value.push(obj)
	// 清空输入框
	sendInfo.value = ""
	// 模拟返回消息,延迟2秒,开发环境下可以把这里替换成网络请求的逻辑
	setTimeout(()=>{
		dialogList.value[dialogList.value.length - 1].output = "别说了,我知道你很帅!"
	},2000)
}

// 触顶加载更多
const addInfo = () => {
	console.log('触顶加载更多!')
}

</script>

<style lang="scss" scoped>
.content {
	width: 100vw;
	height: 100vh;
	background: gainsboro;
	.container {
		width: 100%;
		height: 85vh;
		padding: 10rpx 10rpx 10rpx 20rpx;
		box-sizing: border-box;
		.Scroll {
			width: 100%;
			height: 85vh;
			.box {
				width: 100%;
				padding: 0 10rpx 0 0;
				box-sizing: border-box;
				.output {
					width: 100%;
					display: flex;
					justify-content: flex-start;
					margin: 30rpx 0;
					.o {
						max-width: 70vw;
						height: auto;
						padding: 10rpx;
						background-color: rgb(192,192,192);
						border-top-left-radius: 10rpx;
						border-top-right-radius: 10rpx;
						border-bottom-right-radius: 10rpx;
					}
				}
				.input {
					width: 100%;
					display: flex;
					justify-content: flex-end;
					margin: 20rpx 0;
					.i {
						max-width: 70vw;
						height: auto;
						padding: 10rpx;
						background-color: rgb(28,217,128);
						border-top-left-radius: 10rpx;
						border-top-right-radius: 10rpx;
						border-bottom-left-radius: 10rpx;
					}
				}
			}
		}
	}
	.footer {
		width: 100vw;
		height: 10vh;
		margin-top: 20rpx;
		background-color: #e3f9fd;
		display: flex;
		justify-content: center;
		align-items: center;
		padding: 10rpx 20rpx 0 20rpx;
		box-sizing: border-box;
		.ipt {
			flex: 6;
			height: 100%;
			display: flex;
			justify-content: center;
			align-items: center;
			.IPT {
				height: 80rpx;
				width: 100%;
				line-height: 80rpx;
				background-color: white;
				border-radius: 10rpx;
				margin-right: 10rpx;
				padding-left: 10rpx;
			}
		}
		.btn {
			flex: 1.5;
			.BTN {
				height: 80rpx;
				line-height: 80rpx;
				border-radius: 10rpx;
				margin-right: 10rpx;
			}
		}
	}
}
</style>

二、效果展示

三、描述

整体上就是使用uni-app的scroll-into-view这个方法实现的,通过vue的watch,来监视聊天内容,变化了则更新scroll-into-view属性所关注的id,这里需要动态的给列表渲染的每一项设置一个动态的id,id是不能重复的,大致逻辑就是这样,具体的看代码部分就好,实在看不懂就自己贴一下代码到自己项目,我这也是个demo样式,能直接跑起来的,希望对你有帮助!

GitHub 加速计划 / vu / vue
207.54 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5 * chore: fix link broken Signed-off-by: snoppy <michaleli@foxmail.com> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <michaleli@foxmail.com> Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891 Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐