266 lines
6.5 KiB
Vue
266 lines
6.5 KiB
Vue
<template>
|
||
<view class="chat-page">
|
||
<view class="message-wrap">
|
||
<scroll-view scroll-y enable-flex class="scroll-list" @scrolltolower="loadHistory" :scroll-top="scrollTop">
|
||
<view class="message-item-wrap">
|
||
<view v-for="(item, index) in messageList" :key="item.id" @click="onClick(index)">
|
||
<MessageItem :detail="item"></MessageItem>
|
||
</view>
|
||
</view>
|
||
<view class="loading-wrap" :style="{ display: loadingStatus?'':'none' }">
|
||
<uni-load-more :iconSize="18" :status="loadingStatus" />
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
<!-- 键盘弹起占位 -->
|
||
<view :style="{height: inputBottomValue}" :class="isKeyboardHidden"></view>
|
||
</view>
|
||
<!-- 消息输入 -->
|
||
<view class="textarea-wrap" :class="isKeyboardHidden" :style="{ bottom: inputBottomValue}">
|
||
<!-- #ifdef MP-WEIXIN -->
|
||
<textarea :show-confirm-bar='false' class="textarea" confirm-type="send" :auto-height="true"
|
||
v-model="inputValue" :adjust-position="false" type="text" @confirm="sendMessage" confirm-hold />
|
||
<!-- #endif -->
|
||
<!-- #ifndef MP-WEIXIN -->
|
||
<textarea :show-confirm-bar='false' class="textarea" confirm-type="send" :auto-height="true"
|
||
v-model="inputValue" :adjust-position="platform=='ios'?true:false" type="text" @confirm="sendMessage"
|
||
confirm-hold />
|
||
<!-- #endif -->
|
||
|
||
<view class="send-btn" @tap="sendMessage">
|
||
<image src="@/static/chat/enter_n.png" mode=""></image>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- <view class="textarea-wrap" :style="{ bottom: inputBottomValue}">
|
||
<textarea :show-confirm-bar='false' class="textarea" confirm-type="send" :auto-height="true"
|
||
v-model="inputValue" :cursor-spacing="10" type="text" @confirm="sendMessage" confirm-hold />
|
||
<view class="send-btn" @tap="sendMessage">
|
||
<image src="@/static/chat/enter_n.png" mode=""></image>
|
||
</view>
|
||
</view> -->
|
||
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
nextTick,
|
||
computed
|
||
} from 'vue';
|
||
import {
|
||
onHide,
|
||
onShow,
|
||
onLoad
|
||
} from '@dcloudio/uni-app';
|
||
import MessageItem from './components/messageItem.vue';
|
||
|
||
const inputValue = ref('');
|
||
const scrollTop = ref();
|
||
const platform = ref('')
|
||
onLoad(() => {
|
||
uni.getSystemInfo({
|
||
success: (res) => {
|
||
platform.value = res.platform
|
||
console.log(platform.value);
|
||
}
|
||
});
|
||
});
|
||
|
||
// 发送消息
|
||
const sendMessage = () => {
|
||
if ((inputValue.value ?? '') === '') return
|
||
let newMessage = {
|
||
id: messageList.value.length + 1,
|
||
isRobotMessage: false,
|
||
message: inputValue.value,
|
||
}
|
||
messageList.value.push(newMessage)
|
||
inputValue.value = '';
|
||
// 滚动到底部
|
||
scrollTop.value = 0;
|
||
nextTick(() => {
|
||
scrollTop.value = undefined;
|
||
})
|
||
}
|
||
|
||
const messageList = ref([]); //消息列表
|
||
const getMessageList = (num = 10) => {
|
||
const messageData = new Array(num).fill(0).map((v, i) => {
|
||
const id = messageList.value.length + i
|
||
const random = Math.random();
|
||
if (random < 0.33) {
|
||
return {
|
||
id: id,
|
||
isRobotMessage: true,
|
||
message: `(${id}):您好,我是您的专属客服达达,请问有什么可以帮您?`,
|
||
}
|
||
} else if (random >= 0.33 && random < 0.66) {
|
||
return {
|
||
id: id,
|
||
isRobotMessage: true,
|
||
message: `(${id}): 结果查询中...`,
|
||
}
|
||
} else {
|
||
return {
|
||
id: id,
|
||
isRobotMessage: false,
|
||
message: `(${id}): test english and digital messages. ahsdfhaksjdhfalspwqejhglaksjdf 4236784623847623423423487234234`,
|
||
}
|
||
}
|
||
});
|
||
messageList.value.unshift(...messageData.reverse());
|
||
}
|
||
getMessageList(5)
|
||
|
||
const loadingStatus = ref(); //'more' | 'loading' | 'no-more'
|
||
const loadHistory = (e) => {
|
||
if (loadingStatus.value && loadingStatus.value !== 'more') return
|
||
loadingStatus.value = 'loading';
|
||
setTimeout(() => {
|
||
getMessageList()
|
||
loadingStatus.value = 'more';
|
||
}, 1000)
|
||
}
|
||
|
||
const inputBottomValue = ref('0'); //输入框距离底部距离
|
||
// 计算属性:判断是否应用keyboard-hidden类
|
||
const isKeyboardHidden = computed(() => {
|
||
return inputBottomValue.value === '0' && platform.value === 'ios' ? 'keyboard-hidden' : '';
|
||
});
|
||
|
||
|
||
// 监听键盘高度变化
|
||
const keyboardChange = (res) => {
|
||
// #ifdef MP-WEIXIN
|
||
// res.height 为键盘高度
|
||
if (res.height) { //展开
|
||
inputBottomValue.value = res.height + 'px';
|
||
} else { //收起
|
||
// 键盘收起时,只需要很小的安全区域
|
||
inputBottomValue.value = '0'
|
||
}
|
||
// #endif
|
||
|
||
// #ifndef MP-WEIXIN
|
||
// res.height 为键盘高度
|
||
if (res.height) { //展开
|
||
if (platform.value === 'ios') {
|
||
// iOS平台上要立即设置底部距离,避免出现空隙
|
||
inputBottomValue.value = '0px';
|
||
// 关键:用定时器来设置最终值,这样可以避免闪烁和空隙
|
||
setTimeout(() => {
|
||
inputBottomValue.value = '0px';
|
||
}, 0);
|
||
} else {
|
||
// 非iOS平台保持原来逻辑
|
||
inputBottomValue.value = res.height + 'px';
|
||
}
|
||
} else { //收起
|
||
// 键盘收起时
|
||
inputBottomValue.value = '0';
|
||
}
|
||
// #endif
|
||
}
|
||
onHide(() => {
|
||
uni.offKeyboardHeightChange(keyboardChange);
|
||
})
|
||
onShow(() => {
|
||
uni.onKeyboardHeightChange(keyboardChange);
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.chat-page {
|
||
height: calc(100vh - 160rpx);
|
||
display: flex;
|
||
flex-direction: column;
|
||
background-color: rgb(240, 242, 247);
|
||
position: relative;
|
||
overflow: hidden;
|
||
|
||
.message-wrap {
|
||
height: 0;
|
||
flex: 1;
|
||
|
||
.scroll-list {
|
||
height: 100%;
|
||
transform: scaleY(-1);
|
||
}
|
||
|
||
.message-item-wrap {
|
||
min-height: 100%;
|
||
transform: scaleY(-1);
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.loading-wrap {
|
||
transform: scaleY(-1);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 10rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
.textarea-wrap {
|
||
background-color: #fff;
|
||
width: 100%;
|
||
padding: 25rpx 48rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
position: fixed;
|
||
left: 0;
|
||
transition: all 0.1s ease-out;
|
||
|
||
.textarea {
|
||
flex: 1;
|
||
border-radius: 20rpx;
|
||
background-color: #F1F7FB;
|
||
padding: 15rpx 35rpx;
|
||
min-height: 50rpx;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.send-btn {
|
||
height: 80rpx;
|
||
width: 80rpx;
|
||
padding: 16rpx;
|
||
border-radius: 50%;
|
||
background-color: #0070F0;
|
||
margin-left: 32rpx;
|
||
transition: transform 0.2s ease;
|
||
position: relative;
|
||
z-index: 1;
|
||
|
||
&:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
image {
|
||
height: 100%;
|
||
width: 100%;
|
||
}
|
||
}
|
||
}
|
||
|
||
.keyboard-hidden {
|
||
/* #ifdef APP-PLUS || MP-WEIXIN */
|
||
padding-bottom: calc(env(safe-area-inset-bottom) - 20rpx);
|
||
/* #endif */
|
||
}
|
||
</style>
|
||
<style>
|
||
view {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
page {
|
||
background-color: rgb(240, 242, 247);
|
||
}
|
||
</style> |