Skip to content
本页导读

Chatlist

组件类型:UxChatlistComponentPublicInstance

支持上拉加载聊天记录

平台兼容性

UniApp X

AndroidiOSweb鸿蒙 Next小程序
x

UniApp Vue Nvue

AndroidiOSweb鸿蒙 Next小程序
xxxx

Props

属性名类型默认值说明
defaultSlot默认slot
loadmoreSlotloadmoreslot
upperThresholdNumber50距顶部/左边多远时(单位px),触发scrolltoupper事件
lowerThresholdNumber50距底部/右边多远时(单位px),触发scrolltolower事件
scrollTopNumber0设置竖向滚动条位置
scrollIntoViewString值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素
loadmoreStatesArray上拉加载状态文案
backgroundString背景色
xstyleArray自定义样式

Events

事件名说明参数
loadmore上拉加载触发
scrolltoupper滚动到顶部/左边,会触发scrolltoupper事件
scrolltolower滚动到底部/右边,会触发scrolltolower事件
scroll滚动时触发

示例代码

html
<template>
	<ux-page :stack="showDoc">
		<ux-navbar :title="title" :border="false">
			<template v-slot:right>
				<!-- #ifndef MP -->
				<ux-button theme="text" icon="/static/tip.png" :icon-size="22" @click="onDoc()"></ux-button>
				<!-- #endif -->
			</template>
		</ux-navbar>
		
		<ux-chatlist ref="uxChatlistRef" :scrollTop="scrollTop" @scrolltolower="loadHistory">
			
			<ux-chatlist-item>
				<ux-placeholder :height="60"></ux-placeholder>
			</ux-chatlist-item>
			
			<ux-chatlist-item v-for="(msg, index) in list" :key="index">
				<view :class="msg.isRobot ? 'item':'item-my'">
					<view v-if="msg.isRobot" class="robot-wrap">
						<view class="robot-icon">
							<ux-icon type="soapbubble-filled" color="white"></ux-icon>
						</view>
						<view class="robot-text-b">
							<text class="robot-text">{{ msg.content }}</text>
						</view>
					</view>
					<view v-else class="my-wrap">
						<view class="my-text-b">
							<text class="my-text">{{ msg.content }}</text>
						</view>
					</view>
				</view>
			</ux-chatlist-item>
			
			<ux-chatlist-item style="margin-top: 20px;">
				<ux-card direction="column" icon="flag-filled" title="聊天列表" :bold="true">
					<ux-text text="支持上拉加载聊天记录"></ux-text>
				</ux-card>
			</ux-chatlist-item>
		</ux-chatlist>
		
		<view class="input-wrap" :style="{'background-color': backgroundColor, 'bottom': keyboardHeight + 'px'}">
			<input class="input" type="text" placeholder="请问您有什么问题?" :adjust-position="false" v-model="inputValue" @confirm="sendMessage" @keyboardheightchange="heightChange" />
			<view class="send-btn" @tap="sendMessage">
				<ux-icon type="navigation-filled" color="white"></ux-icon>
			</view>
		</view>
	</ux-page>
</template>
<template>
	<ux-page :stack="showDoc">
		<ux-navbar :title="title" :border="false">
			<template v-slot:right>
				<!-- #ifndef MP -->
				<ux-button theme="text" icon="/static/tip.png" :icon-size="22" @click="onDoc()"></ux-button>
				<!-- #endif -->
			</template>
		</ux-navbar>
		
		<ux-chatlist ref="uxChatlistRef" :scrollTop="scrollTop" @scrolltolower="loadHistory">
			
			<ux-chatlist-item>
				<ux-placeholder :height="60"></ux-placeholder>
			</ux-chatlist-item>
			
			<ux-chatlist-item v-for="(msg, index) in list" :key="index">
				<view :class="msg.isRobot ? 'item':'item-my'">
					<view v-if="msg.isRobot" class="robot-wrap">
						<view class="robot-icon">
							<ux-icon type="soapbubble-filled" color="white"></ux-icon>
						</view>
						<view class="robot-text-b">
							<text class="robot-text">{{ msg.content }}</text>
						</view>
					</view>
					<view v-else class="my-wrap">
						<view class="my-text-b">
							<text class="my-text">{{ msg.content }}</text>
						</view>
					</view>
				</view>
			</ux-chatlist-item>
			
			<ux-chatlist-item style="margin-top: 20px;">
				<ux-card direction="column" icon="flag-filled" title="聊天列表" :bold="true">
					<ux-text text="支持上拉加载聊天记录"></ux-text>
				</ux-card>
			</ux-chatlist-item>
		</ux-chatlist>
		
		<view class="input-wrap" :style="{'background-color': backgroundColor, 'bottom': keyboardHeight + 'px'}">
			<input class="input" type="text" placeholder="请问您有什么问题?" :adjust-position="false" v-model="inputValue" @confirm="sendMessage" @keyboardheightchange="heightChange" />
			<view class="send-btn" @tap="sendMessage">
				<ux-icon type="navigation-filled" color="white"></ux-icon>
			</view>
		</view>
	</ux-page>
</template>
ts
<script setup>
	import * as plus from '@/uni_modules/ux-plus'
	import {useBackgroundColor} from '@/uni_modules/ux-frame'
	
	type Msg = {
		isRobot: boolean,
		content: string
	}
	
	const title = ref('')
	
	const list = ref<Msg[]>([] as Msg[])
	
	const uxChatlistRef = ref<UxChatlistComponentPublicInstance | null>(null)
	const keyboardHeight = ref(0)
	const inputValue = ref('')
	const scrollTop = ref(0)
	const lastPage = ref(false)
	
	const backgroundColor = computed((): string => {
		return useBackgroundColor('#fff', '#222')
	})
	
	function send(robot: boolean, content: string) {
		list.value.unshift({
			isRobot: robot,
			content: content
		} as Msg)
	}
	
	function reply(msg: string) {
		const random = Math.random()
		
		if(random < 0.4) {
			send(true, '智障自动回复:' + msg)
		}
	}
	
	function sendMessage() {
		keyboardHeight.value = 0
		if (inputValue.value == '') return
		
		send(false, inputValue.value)
		
		reply(inputValue.value)
		
		inputValue.value = ''
		
		uxChatlistRef.value?.$callMethod('scrollTo')
	}
	
	function loadHistory() {
		uni.request<UTSJSONObject[]>({
			url: 'https://unidemo.dcloud.net.cn/api/news?column=title,author_name,cover,published_at',
			method:"GET",
			success: (res: RequestSuccess<UTSJSONObject[]>) => {
				if (res.statusCode == 200) {
					const result = res.data
					
					if(result != null){
						if(list.value.length >= 50) {
							lastPage.value = true
							return
						}
						
						result.forEach((e: UTSJSONObject) => {
							list.value.push({
								isRobot: Math.random() < 0.4,
								content: e['title']! as string
							} as Msg)
						})
					}
					
					lastPage.value = false
				}
			},
			complete: () => {
				uxChatlistRef.value?.$callMethod('loadSuccess', lastPage.value)
			}
		});
	}
	
	function heightChange(e: InputKeyboardHeightChangeEvent) {
		keyboardHeight.value = e.detail.height
	}
	
	const showDoc = ref(false)
	
	function onDoc() {
		plus.openWeb({
			title: '在线文档',
			url: 'https://www.uxframe.cn/component/chatlist.html',
			// blur: 1,
			success: () => {
				showDoc.value = true
			},
			complete: () => {
				showDoc.value = false
			}
		})
	}
	
	onLoad((e) => {
		title.value = e['title'] ?? ''
		
		send(true, '您好,我是您的专属客服,请问有什么可以帮您?请问有什么可以帮您?')
	})
</script>
<script setup>
	import * as plus from '@/uni_modules/ux-plus'
	import {useBackgroundColor} from '@/uni_modules/ux-frame'
	
	type Msg = {
		isRobot: boolean,
		content: string
	}
	
	const title = ref('')
	
	const list = ref<Msg[]>([] as Msg[])
	
	const uxChatlistRef = ref<UxChatlistComponentPublicInstance | null>(null)
	const keyboardHeight = ref(0)
	const inputValue = ref('')
	const scrollTop = ref(0)
	const lastPage = ref(false)
	
	const backgroundColor = computed((): string => {
		return useBackgroundColor('#fff', '#222')
	})
	
	function send(robot: boolean, content: string) {
		list.value.unshift({
			isRobot: robot,
			content: content
		} as Msg)
	}
	
	function reply(msg: string) {
		const random = Math.random()
		
		if(random < 0.4) {
			send(true, '智障自动回复:' + msg)
		}
	}
	
	function sendMessage() {
		keyboardHeight.value = 0
		if (inputValue.value == '') return
		
		send(false, inputValue.value)
		
		reply(inputValue.value)
		
		inputValue.value = ''
		
		uxChatlistRef.value?.$callMethod('scrollTo')
	}
	
	function loadHistory() {
		uni.request<UTSJSONObject[]>({
			url: 'https://unidemo.dcloud.net.cn/api/news?column=title,author_name,cover,published_at',
			method:"GET",
			success: (res: RequestSuccess<UTSJSONObject[]>) => {
				if (res.statusCode == 200) {
					const result = res.data
					
					if(result != null){
						if(list.value.length >= 50) {
							lastPage.value = true
							return
						}
						
						result.forEach((e: UTSJSONObject) => {
							list.value.push({
								isRobot: Math.random() < 0.4,
								content: e['title']! as string
							} as Msg)
						})
					}
					
					lastPage.value = false
				}
			},
			complete: () => {
				uxChatlistRef.value?.$callMethod('loadSuccess', lastPage.value)
			}
		});
	}
	
	function heightChange(e: InputKeyboardHeightChangeEvent) {
		keyboardHeight.value = e.detail.height
	}
	
	const showDoc = ref(false)
	
	function onDoc() {
		plus.openWeb({
			title: '在线文档',
			url: 'https://www.uxframe.cn/component/chatlist.html',
			// blur: 1,
			success: () => {
				showDoc.value = true
			},
			complete: () => {
				showDoc.value = false
			}
		})
	}
	
	onLoad((e) => {
		title.value = e['title'] ?? ''
		
		send(true, '您好,我是您的专属客服,请问有什么可以帮您?请问有什么可以帮您?')
	})
</script>
css
<style lang="scss">
	.item {
		flex: 1;
		display: flex;
		flex-direction: row;
		justify-content: flex-start;
		padding: 0 30px 16px 17px;
	}
	
	.robot-wrap {
		/* #ifndef APP */
		max-width: 100%;
		/* #endif */
		/* #ifdef APP */
		max-width: 600rpx;
		/* #endif */
		display: flex;
		flex-direction: row;
	}
	
	.robot-icon {
		height: 35px;
		width: 35px;
		margin-right: 8px;
		display: flex;
		justify-content: center;
		align-items: center;
		background-color: #0070F0;
		border-radius: 35px;
	}
	
	.robot-text-b {
		flex: 1;
		background-color: #fff;
		padding: 15px;
		border-radius: 0 24px 24px 24px;
	}
	
	.robot-text {
		flex: 1;
		color: #303437;
		font-size: 15px;
		/* #ifndef APP */
		word-break: break-all;
		/* #endif */
	}
	
	.item-my {
		flex: 1;
		display: flex;
		flex-direction: row;
		justify-content: flex-end;
		padding: 0 30px 16px 17px;
	}
	
	.my-wrap {
		/* #ifndef APP */
		max-width: 100%;
		/* #endif */
		/* #ifdef APP */
		max-width: 600rpx;
		/* #endif */
		display: flex;
		flex-direction: row;
	}
	
	
	.my-text-b {
		background-color: #0070F0;
		border-radius: 24px 24px 0 24px;
		padding: 15px;
	}
	
	.my-text {
		flex: 1;
		text-align: right;
		font-size: 15px;
		color: #fff;
		/* #ifndef APP */
		word-break: break-all;
		/* #endif */
	}
	
	.input-wrap {
		position: fixed;
		bottom: 0;
		height: 60px;
		width: 100%;
		padding: 0 24px;
		display: flex;
		flex-direction: row;
		align-items: center;
		background-color: #fff;
	
		.input {
			flex: 1;
			height: 44px;
			border-radius: 22px;
			background-color: #F1F7FB;
			padding: 0 20px;
		}
	
		.send-btn {
			height: 44px;
			width: 44px;
			padding: 10px;
			border-radius: 22px;
			background-color: #0070F0;
			margin-left: 16px;
			display: flex;
			align-items: center;
			justify-content: center;
		}
	}
</style>
<style lang="scss">
	.item {
		flex: 1;
		display: flex;
		flex-direction: row;
		justify-content: flex-start;
		padding: 0 30px 16px 17px;
	}
	
	.robot-wrap {
		/* #ifndef APP */
		max-width: 100%;
		/* #endif */
		/* #ifdef APP */
		max-width: 600rpx;
		/* #endif */
		display: flex;
		flex-direction: row;
	}
	
	.robot-icon {
		height: 35px;
		width: 35px;
		margin-right: 8px;
		display: flex;
		justify-content: center;
		align-items: center;
		background-color: #0070F0;
		border-radius: 35px;
	}
	
	.robot-text-b {
		flex: 1;
		background-color: #fff;
		padding: 15px;
		border-radius: 0 24px 24px 24px;
	}
	
	.robot-text {
		flex: 1;
		color: #303437;
		font-size: 15px;
		/* #ifndef APP */
		word-break: break-all;
		/* #endif */
	}
	
	.item-my {
		flex: 1;
		display: flex;
		flex-direction: row;
		justify-content: flex-end;
		padding: 0 30px 16px 17px;
	}
	
	.my-wrap {
		/* #ifndef APP */
		max-width: 100%;
		/* #endif */
		/* #ifdef APP */
		max-width: 600rpx;
		/* #endif */
		display: flex;
		flex-direction: row;
	}
	
	
	.my-text-b {
		background-color: #0070F0;
		border-radius: 24px 24px 0 24px;
		padding: 15px;
	}
	
	.my-text {
		flex: 1;
		text-align: right;
		font-size: 15px;
		color: #fff;
		/* #ifndef APP */
		word-break: break-all;
		/* #endif */
	}
	
	.input-wrap {
		position: fixed;
		bottom: 0;
		height: 60px;
		width: 100%;
		padding: 0 24px;
		display: flex;
		flex-direction: row;
		align-items: center;
		background-color: #fff;
	
		.input {
			flex: 1;
			height: 44px;
			border-radius: 22px;
			background-color: #F1F7FB;
			padding: 0 20px;
		}
	
		.send-btn {
			height: 44px;
			width: 44px;
			padding: 10px;
			border-radius: 22px;
			background-color: #0070F0;
			margin-left: 16px;
			display: flex;
			align-items: center;
			justify-content: center;
		}
	}
</style>