| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- <template>
- <view class="page">
- <!-- 空盒子用来防止消息过少时 拉起键盘会遮盖消息 -->
- <view :animation="anData" style="height:0;">
- </view>
- <!-- 消息体 -->
- <scroll-view scroll-with-animation scroll-y="true" @touchmove="hideKey" style="width: 750rpx;"
- :style="{'height':srcollHeight}" :scroll-top="go">
- <!-- 用来获取消息体高度 -->
- <view id="okk" scroll-with-animation>
- <!-- 消息 -->
- <view class="flex-column-start" v-for="(x,i) in msgList" :key="i">
- <!-- 用户消息 头像可选加入-->
- <view v-if="x.my" class="flex justify-end padding-right one-show align-start padding-top">
- <!-- <image v-if="!x.my" class="chat-img" src="../../static/..." mode="aspectFill" ></image> -->
- <view class="flex justify-end" style="width: 400rpx;">
- <view class="margin-left padding-chat bg-blue" style="border-radius: 6px;">
- <text style="word-break: break-all;">{{x.msg}}</text>
- </view>
- </view>
- </view>
- <!-- 机器人消息 -->
- <view v-if="!x.my" class=" margin-left margin-top one-show">
- <view class="dis j-c a-c mt-1 mb-3 ">
- <text class="initTime">{{x.initTime}}</text>
- </view>
- <view class="flex" style="max-width: 650rpx;">
- <view class=" chatBox dis j-s a-c" style="">
- <image v-if="i==0" src="/static/image/customerService1.png" mode=""
- style="width: 47px;height: 50px;position: absolute;left: 0;bottom: 0;">
- </image>
- <text :style="{ marginLeft: i === 0 ? '40px' : '0px' }">{{x.msg}}</text>
- </view>
- </view>
- </view>
- </view>
- <!-- loading是显示 -->
- <view v-show="msgLoad" class="flex-row-start margin-left margin-top">
- <!-- <view class="chat-img flex-row-center">
- <image style="height: 75rpx;width: 75rpx;" src="../../static/image/robt.png" mode="aspectFit">
- </image>
- </view> -->
- <view class="flex" style="width: 500rpx;">
- <view class="margin-left padding-chat flex-column-start"
- style="border-radius: 35rpx;background-color: #f9f9f9;">
- <view class="cuIcon-loading turn-load" style="font-size: 35rpx;color: #0081ff;">
- </view>
- </view>
- </view>
- </view>
- <!-- 防止消息底部被遮 -->
- <view style="height: 100px;">
- </view>
- </view>
- </scroll-view>
- <!-- 底部导航栏 -->
- <view class="flex-column-center" style="position: fixed;bottom: -180px;" :animation="animationData">
- <view class="bottom-dh-char flex-row-around" style="font-size: 55rpx;">
- <!-- vue无法使用软键盘"发送" -->
- <input v-model="msg" class="dh-input" type="text" style="background-color: #f0f0f0;" @confirm="sendMsg"
- confirm-type="search" placeholder-class="my-neirong-sm" placeholder="请描述您的问题" />
- <view class="bg-blue "
- style="border-radius: 6px;font-size: 12px;padding: 5px 10px;box-sizing: border-box;"
- @click="sendMsg">
- 发送
- </view>
- </view>
- <!-- 附加栏(自定义) -->
- <view class="box-normal flex-row-around flex-wrap">
- <view class="tb-text">
- <view class="cuIcon-form"></view>
- <text>问题反馈</text>
- </view>
- <view class="tb-text">
- <view class="cuIcon-form"></view>
- <text>人工客服</text>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script>
- // rpx和px的比率
- var l
- // 可用窗口高度
- var wh
- // 顶部空盒子的高度
- var mgUpHeight
- export default {
- onLoad() {
- // 如果需要缓存消息缓存msgList即可
- // 监听键盘拉起
- // 因为无法控制键盘拉起的速度,所以这里尽量以慢速处理
- uni.onKeyboardHeightChange(res => {
- const query = uni.createSelectorQuery()
- query.select('#okk').boundingClientRect(data => {
- // 若消息体没有超过2倍的键盘则向下移动差值,防止遮住消息体
- var up = res.height * 2 - data.height - l * 110
- if (up > 0) {
- // 动态改变空盒子高度
- this.msgMove(up, 300)
- // 记录改变的值,若不收回键盘且发送了消息用来防止消息过多被遮盖
- mgUpHeight = up
- }
- // 收回
- if (res.height == 0) {
- this.msgMove(0, 0)
- }
- }).exec();
- })
- var query = uni.getSystemInfoSync()
- l = query.screenWidth / 750
- wh = query.windowHeight
- this.srcollHeight = query.windowHeight + "px"
- },
- data() {
- return {
- realTime: "",
- msgLoad: false,
- anData: {},
- animationData: {},
- showTow: false,
- // 消息体,定义机器人初次的消息(或者自定义出现时机)
- // my->谁发的消息 msg->消息文本 type->客服消息模板类型 questionList->快速获取问题答案的问题列表
- msgList: [{
- my: false,
- msg: "您好,掌柜智能客服助理为您服务!",
- initTime: this.getFormattedTime(),
- }],
- msg: "",
- go: 0,
- srcollHeight: 0
- }
- },
- methods: {
- getFormattedTime() {
- const date = new Date();
- const year = date.getFullYear();
- const month = (date.getMonth() + 1).toString().padStart(2, '0');
- const day = date.getDate().toString().padStart(2, '0');
- const hours = date.getHours().toString().padStart(2, '0');
- const minutes = date.getMinutes().toString().padStart(2, '0');
- return `${year}-${month}-${day} ${hours}:${minutes}`;
- },
- // 切换输入法时移动输入框(按照官方的上推页面的原理应该会自动适应不同的键盘高度-->官方bug)
- goPag(kh) {
- this.upTowmn(0, 250)
- if (this.keyHeight != 0) {
- if (kh - this.keyHeight > 0) {
- this.upTowmn(this.keyHeight - kh, 250)
- }
- }
- },
- // 移动顶部的空盒子
- msgMove(x, t) {
- var animation = uni.createAnimation({
- duration: t,
- timingFunction: 'linear',
- })
- this.animation = animation
- animation.height(x).step()
- this.anData = animation.export()
- },
- // 保持消息体可见
- msgGo() {
- const query = uni.createSelectorQuery()
- // 延时100ms保证是最新的高度
- setTimeout(() => {
- // 获取消息体高度
- query.select('#okk').boundingClientRect(data => {
- // 如果超过scorll高度就滚动scorll
- if (data.height - wh > 0) {
- this.go = data.height - wh
- }
- // 保证键盘第一次拉起时消息体能保持可见
- var moveY = wh - data.height
- // 超出页面则缩回空盒子
- if (moveY - mgUpHeight < 0) {
- // 小于0则视为0
- if (moveY < 0) {
- this.msgMove(0, 200)
- } else {
- // 否则缩回盒子对应的高度
- this.msgMove(moveY, 200)
- }
- }
- }).exec();
- }, 100)
- },
- // 回答问题的业务逻辑
- answer(id) {
- // 这里应该传入问题的id,模拟就用index代替了
- },
- sendMsg() {
- // 消息为空不做任何操作
- if (this.msg == "") {
- return 0;
- }
- // 显示消息 msg消息文本,my鉴别是谁发的消息(不能用俩个消息数组循环,否则消息不会穿插)
- this.msgList.push({
- "msg": this.msg,
- "my": true
- })
- // 保证消息可见
- this.msgGo()
- // 回答问题
- this.msgKf(this.msg)
- // 清除消息
- this.msg = ""
- },
- async msgKf(x) {
- // loading
- this.msgLoad = true
- // 这里连接服务器获取答案
- let res = await this.$http.post('/customer/ai/sendMessage', {
- message: this.msg
- });
- if (res.code == 200) {
- // 下面模拟请求
- setTimeout(() => {
- // 取消loading
- this.msgLoad = false
- const newMessage = {
- my: false,
- msg: '',
- initTime: res.data.date
- };
- this.msgList.push(newMessage);
- this.msgGo();
- // 使用typeWriter效果逐个字符显示
- this.typeWriter(res.data.message, newMessage);
- }, 400)
- }
- },
- // 不建议输入框聚焦时操作此动画
- ckAdd() {
- if (!this.showTow) {
- this.upTowmn(-180, 350)
- } else {
- this.upTowmn(0, 200)
- }
- this.showTow = !this.showTow
- },
- hideKey() {
- uni.hideKeyboard()
- },
- // 拉起/收回附加栏
- upTowmn(x, t) {
- var animation = uni.createAnimation({
- duration: t,
- timingFunction: 'ease',
- })
- this.animation = animation
- animation.translateY(x).step()
- this.animationData = animation.export()
- },
- typeWriter(text, messageObj) {
- let index = 0;
- const speed = 20;
- const addCharacter = () => {
- if (index < text.length) {
- messageObj.msg += text.charAt(index);
- index++;
- setTimeout(addCharacter, speed);
- } else {
- this.msgGo();
- }
- };
- addCharacter();
- },
- }
- }
- </script>
- <style lang="scss" scoped>
- @import "./css/icon.css";
- @import "./css/main.css";
- @import "./css/index-app.css";
- .chatBox {
- padding: 8px 14px;
- border-radius: 35rpx;
- background-color: #fff;
- box-shadow: 0px 2px 5px 0px #DAE3F4;
- border-radius: 6px;
- font-size: 12px;
- position: relative;
- text {
- color: #000;
- word-break: break-all;
- }
- }
- .initTime {
- background: rgba(207, 207, 207, 0.2);
- border-radius: 20px;
- width: auto;
- padding: 2px 8px;
- color: #000;
- font-size: 12px;
- }
- .page {
- background-color: #F8FAFE;
- }
- .bottom-dh-char {
- background: #FFFFFF;
- box-shadow: 0px -4px 10px 0px rgba(218, 227, 244, 0.2);
- border-radius: 0px 0px 0px 0px;
- width: 750rpx;
- height: 110rpx;
- }
- .center-box {
- width: 720rpx;
- padding-left: 25rpx;
- }
- .hui-box {
- width: 750rpx;
- height: 100%;
- }
- .dh-input {
- width: 80%;
- height: 33px;
- border-radius: 6px;
- padding-left: 15rpx;
- background-color: transparent !important;
- border: 1px solid #EBEBEB;
- }
- .box-normal {
- width: 750rpx;
- height: 180px;
- background-color: #FFFFFF;
- }
- .tb-text view {
- font-size: 65rpx;
- }
- .tb-text text {
- font-size: 25rpx;
- color: #737373;
- }
- .chat-img {
- border-radius: 50%;
- width: 100rpx;
- height: 100rpx;
- background-color: #f7f7f7;
- }
- .padding-chat {
- padding: 17rpx 20rpx;
- }
- .tb-nv {
- width: 50rpx;
- height: 50rpx;
- }
- </style>
|