Chatlist
组件类型:UxChatlistComponentPublicInstance
支持上拉加载聊天记录
平台兼容性
UniApp X
Android | iOS | web | 鸿蒙 Next | 小程序 |
---|---|---|---|---|
√ | √ | √ | x | √ |
UniApp Vue Nvue
Android | iOS | web | 鸿蒙 Next | 小程序 |
---|---|---|---|---|
x | x | √ | x | x |
Props
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
default | Slot | 默认slot | |
loadmore | Slot | loadmoreslot | |
upperThreshold | Number | 50 | 距顶部/左边多远时(单位px),触发scrolltoupper事件 |
lowerThreshold | Number | 50 | 距底部/右边多远时(单位px),触发scrolltolower事件 |
scrollTop | Number | 0 | 设置竖向滚动条位置 |
scrollIntoView | String | 值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素 | |
loadmoreStates | Array | 上拉加载状态文案 | |
background | String | 背景色 | |
xstyle | Array | 自定义样式 |
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>