uniapp模拟阿里云聊天机器人回复

293 阅读4分钟
<scroll-view class="scroll-view" :scroll-y="true"
    :scroll-top="scrollTop" :scroll-with-animation="true">
    <view class="official-content">
        <view class="title">
            猜你想问
        </view>
        <view class="guess" v-for="(item,index) in guessList" :key='index' @click="sendMessage(item)">
            {{item}}
        </view>
        <view class="title">
            常见问题
        </view>
        <view class="list">
            <view class="question flex-justify" v-for="(item,index) in questions" :key="index"
                @click="sendMessage(item)">
                <view>
                    {{item}}
                </view>
                <view class="right-arrow"></view>
            </view>
        </view>
        <view class="message" v-if="messageList.length>0">
            <view :class="{'text-right':1===item.source}" v-for="(item,index) in messageList"
                :key="index">
                <view class="time">{{$util.timeStampTurnTime(item.time,'hourMinute')}}</view>
                <!-- 富文本 -->
                <view :class="1===item.source?'request':'response'" v-html="item.text"
                    v-if="'RICH_TEXT'===item.contentType">
                </view>
                <!-- 猜你想问 -->
                <view :class="1===item.source?'request':'response'"
                    v-else-if="'Recommend'===item.answerType">
                    <view class="recommend">
                        <view class="text">
                            {{item.text}}
                        </view>
                        <view class="info" @click="sendMessage(recommend.title)"
                            v-for="(recommend,rindex) in item.recommends" :key="rindex">
                            {{recommend.title}}
                        </view>
                    </view>
                </view>
                <!-- 卡片 -->
                <view :class="1===item.source?'request':'response'"
                    v-else-if="'CARD_TEMPLATE'===item.contentType">
                    <!-- 人物卡片 -->
                    <view class="qa-tabs" v-if="'qa-tabs'===item.cardType">
                        <view class="img">
                            <image :src="item.avatar" mode="widthFix"></image>
                            <view class="name">
                                {{item.name}}
                            </view>
                        </view>
                        <view class="tabs">
                            <view class="item" :class="{'active':tabIndex===tindex}"
                                v-for="(tab,tindex) in item.tabs" :key='tindex'
                                @click="tabChange(tab.url,index)">
                                {{tab.name}}
                            </view>
                        </view>
                        <view class="text">
                            {{item.tabs[tabIndex].content}}
                        </view>
                    </view>
                    <!-- 短分点卡片 -->
                    <view class="qa-list" v-if="'qa-list'===item.cardType">
                        <view class="qa-list-item" v-for="($list,lindex) in item.list" :key='lindex'>
                            <view class="qa-list-title flex-align"><text></text> {{$list.title}}</view>
                            <view class="content">
                                {{$list.content}}
                            </view>
                        </view>
                    </view>
                    <!-- 长卡片 -->
                    <view class="qa-collapse-list" v-if="'qa-collapse-list'===item.cardType">
                        <view class="qa-collapse-list-item" v-for="($list,lindex) in item.list"
                            :key='lindex' @click="openPopup($list)">
                            <text class="down-arrow"></text> {{$list.title}}
                        </view>
                    </view>
                    <!-- 表格 -->
                    <view class="qa-table" v-if="'qa-table'===item.cardType">
                        <view>{{item.title}}</view>
                        <view class="qa-table-item flex-align" v-for="($list,lindex) in item.list"
                            :key='lindex'>
                            <view>{{$list.title}}</view>
                            <view>{{$list.content}}</view>
                        </view>
                    </view>
                    <!-- 流程 -->
                    <view class="qa-progress" v-if="'qa-progress'===item.cardType">
                        <view>{{item.title}}</view>
                        <view class="detail mb-3 pl-5" v-for="($list,lindex) in item.list"
                            :key="lindex">
                            <view>
                                {{ $list.title }}
                            </view>
                            <view>
                                {{ $list.desc }}
                            </view>
                        </view>
                    </view>
                    <!-- 快捷回复 -->
                    <view class="qa-slot" v-if="'slot'===item.cardType">
                        <view class="content text-ellipsis">{{item.content}}</view>
                        <view class="flex-wrap">
                            <view class="item flex-resize" v-for="($list,lindex) in item.list"
                                :key="lindex" @click="sendMessage($list.title)">
                                <view>
                                    {{ $list.title }}
                                </view>
                                <view class="font-size-center-10">
                                    {{ $list.desc }}
                                </view>
                            </view>
                        </view>
                    </view>
                    <!-- 图文按钮 -->
                    <view class="adaptable-action-card" v-if="'adaptable-action-card'===item.cardType">
                        <image :src="item.picUrl" mode="widthFix"></image>
                        <view class="p-5">
                            <view class="adaptable-action-card-title">{{item.title}}</view>
                            <view class="adaptable-action-card-content">{{item.content}}</view>
                            <view
                                :class="1==item.actionOrientation ? 'flex-wrap transverse':'vertical'">
                                <view class="item"
                                    :class="$list.style+ (1==item.actionOrientation ? ' flex-resize':'')"
                                    v-for="($list,lindex) in item.actionList" :key="lindex"
                                    @click="'openWindow'===$list.action?gotoPage($list.param.url):sendMessage($list.param.text)">
                                    {{ $list.text }}
                                </view>
                            </view>
                        </view>
                    </view>
                </view>
                <!-- 文本 -->
                <view :class="1===item.source?'request':'response'" v-else>
                    {{item.text}}
                </view>
            </view>
        </view>
    </view>
</scroll-view>
<uni-popup ref="collapse">
    <view class="collapse-popup">
        <view class="text-white">{{info.title}}</view>
        <view class="text-default">{{info.content}}</view>
        <uni-icons class="close" type="closeempty" color="#444" size="30"
            @click="$refs.collapse.close()"></uni-icons>
    </view>
</uni-popup>
<view class="fixed-btn flex-center over-max-width px-5">
    <input class="uni-input" maxlength="50" placeholder-style="color:#999" v-model="inputValue"
        confirm-type="search" placeholder="请描述您的问题" @confirm="inputChange" />
</view>

data() {
    return {
        tabIndex: 0,
        scrollTop: 0,
        scrollHeight: 0,
        inputValue: '',
        guessList: [],
        questions: [],
        info: {},
        title: '',
        messageList: []
    }
},
 mounted() {
    this.initScrollHeight();
},
methods: {
    // 获取聊天框高度
    initScrollHeight() {
        uni.createSelectorQuery()
            .in(this)
            .select('.scroll-view')
            .boundingClientRect(data => {
                if (data) {
                    this.scrollHeight = data.height
                }
            })
            .exec();
    },
    // 滚动到底部
    initContentHeight() {
        uni.createSelectorQuery()
            .in(this)
            .select('.official-content')
            .boundingClientRect(data => {
                if (data) {
                    // 判断内容与聊天框相差高度
                    let top = data.height - this.scrollHeight;
                    // 如果超出聊天框,则滚动
                    if (top > 0) {
                        this.scrollTop = top;
                    }
                }
            })
            .exec();
    },
    // 发送信息
    sendMessage(item) {
        // 讲问题记录到信息列表
        this.messageList.push({
            text: item,
            time: new Date().getTime() / 1000,
            source: 1
        })
        let that = this
        this.$nextTick(() => {
            setTimeout(() => {
                // 发完后滚动到底部查看最近消息
                that.initContentHeight()
            }, 10)
        })
        // todo 请求接口,对接口返回的信息进行处理
        let message = ''
        res.data.messages.forEach(item => {
            let cardType = ''
            let knowledgeText = item.knowledge.content
            // 卡片信息
            if ('CARD_TEMPLATE' === item.knowledge.contentType) {
                knowledgeText = JSON.parse(knowledgeText)
                cardType = knowledgeText.code;
            }
            // 整理此信息数据
            let content = {
                time: new Date().getTime() / 1000,
                source: 2,
                answerType: item.answerType,
                contentType: item.knowledge.contentType,
                cardType: cardType
            }
            // 猜你想问
            if ('Recommend' === item.answerType) {
                this.messageList.push({
                    ...content,
                    text: item.title,
                    recommends: item.recommends
                })
            }
            if ('Knowledge' === item.answerType) {
                if (cardType) {
                    // 人物卡片
                    if ('qa-tabs' === cardType) {
                        knowledgeText.data.tabs.map(item => {
                            item.name = item.title.split('url=')[0]
                            item.url = item.title.split('url=')[1]
                            return item
                        })
                    }
                    // 信息更新
                    this.messageList.push({
                        ...content,
                        ...knowledgeText.data
                    })
                } else {
                    // 更新其他类型信息
                    this.messageList.push({
                        ...content,
                        text: knowledgeText
                    })
                }
            }
            // 纯文本信息
            if ("Text" === item.answerType) {
                this.messageList.push({
                    ...content,
                    text: item.text.content
                })
            }
        })
        // 滚动到顶部
        let that = this
        this.$nextTick(() => {
            setTimeout(() => {
                that.initContentHeight()
            }, 10)
        })
               
    },
    inputChange(e) {
        if (e.detail.value) {
            this.sendMessage(e.detail.value)
        }
        this.inputValue = ''
    },
    tabChange(url, index) {
        this.tabIndex = index;
        this.gotoPage(url)
    },
    openPopup(item) {
        this.info = item
        this.$refs.collapse.open()
    },
    gotoPage(url){
        uni.redirectTo({
            url
        });
    }
}
.official {
    .scroll-view {
        // 聊天框高度
        height: calc(100vh - 500rpx);
        background-color: #fff;
    }

    &-content {
        padding: 0 30rpx 30rpx;

        .title {
            color: #090d0e;
            font-size: 30rpx;
            font-weight: bold;
            padding-top: 40rpx;
        }

        .guess {
            display: inline-block;
            color: #45588a;
            background-color: #f1f5ff;
            font-size: 26rpx;
            margin-right: 30rpx;
            margin-top: 20rpx;
            padding: 20rpx 50rpx;
            border-radius: 40rpx;
        }

        .list {
            box-shadow: 0 0 8rpx 5rpx rgba(0, 0, 0, 0.05);
            border-radius: 30rpx;
            margin-top: 20rpx;
            padding: 10rpx 30rpx;

            .question {
                border-bottom: 1px solid #eeeeee;

                &:last-child {
                    border-bottom: none;
                }

                view {
                    color: #333333;
                    font-size: 26rpx;
                    line-height: 80rpx;
                }

                .right-arrow {
                    &::after {
                        width: 10rpx;
                        height: 10rpx;
                        border-color: #666666;
                    }
                }
            }
        }

        .message {
            .time {
                color: #888888;
                font-size: 24rpx;
                text-align: center;
                margin-top: 40rpx;
                margin-bottom: 20rpx;
            }

            @mixin item {
                max-width: 100%;
                display: inline-block;
                color: #45588a;
                font-size: 26rpx;
                padding: 30rpx;
                background-color: #f1f5ff;
            }

            .request {
                @include item;
                border-radius: 42rpx 42rpx 4rpx 42rpx;
                text-align: left;
            }

            .response {
                @include item;
                border-radius: 42rpx 42rpx 42rpx 4rpx;
            }

            .recommend {
                width: calc(100vw - 120rpx);
                border-radius: 30rpx;

                .text {
                    color: rgba(0, 0, 0, 0.4);
                    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
                    padding: 8rpx 10px;
                }

                .info {
                    padding: 20rpx 10px;
                    color: rgba(0, 0, 0, 0.87);
                    font-size: 34rpx;
                    font-weight: 500;
                    line-height: 1.5;
                }
            }

            .qa-list {
                &-item {
                    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
                    padding-bottom: 20rpx;
                    margin-bottom: 20rpx;

                    &:last-child {
                        border-bottom: none;
                        padding-bottom: 0;
                        margin-bottom: 0;
                    }
                }

                &-title {
                    font-weight: 700;
                    margin-bottom: 10rpx;

                    text {
                        width: 28rpx;
                        height: 28rpx;
                        display: inline-block;
                        border-radius: 50%;
                        background-color: rgb(255, 236, 0);
                        margin-right: 8rpx;
                    }
                }

                .content {
                    margin-left: 36rpx;
                    line-height: 1.5;
                }
            }

            .qa-collapse-list {
                &-item {
                    font-weight: 500;
                    padding: 20rpx 0;
                    border-bottom: 1px solid rgba(0, 0, 0, 0.05);

                    &:first-child {
                        padding-top: 0;
                    }

                    &:last-child {
                        border-bottom: none;
                        padding-bottom: 0;
                    }

                    text {
                        margin-right: 20rpx;
                    }
                }
            }

            .qa-table {
                &-item {
                    padding: 20rpx 0;
                    border-bottom: 1px solid rgba(0, 0, 0, 0.05);

                    &:last-child {
                        border-bottom: none;
                        padding-bottom: 0;
                    }

                    view {
                        font-size: 32rpx;

                        &:first-child {
                            font-weight: 700;
                            margin-right: 16rpx;
                        }
                    }
                }
            }

            .qa-tabs {
                .img {
                    width: 256rpx;
                    background-color: #fff;
                    border-radius: 30rpx;
                    text-align: center;
                    padding: 34rpx 0;

                    image {
                        width: 192rpx;
                    }

                    .name {
                        margin-top: 16rpx;
                    }
                }

                .tabs {
                    margin: 25rpx 0;

                    .item {
                        display: inline-block;
                        color: rgba(0, 0, 0, 0.54);
                        background-color: #fff;
                        border-radius: 40rpx;
                        padding: 10rpx 30rpx;
                        margin-right: 10rpx;
                        font-size: 34rpx;
                        font-weight: 500;
                    }

                    .active {
                        color: rgba(0, 0, 0, 0.87);
                        background-color: rgb(252, 224, 150);
                    }
                }

                .text {
                    background-color: #fff;
                    border-radius: 30rpx;
                    padding: 30rpx;
                    line-height: 1.3;
                }
            }

            .qa-progress {
                position: relative;

                &::before {
                    width: 4rpx;
                    height: calc(100% - 130rpx);
                    background-color: #ffe48c;
                    content: '';
                    display: block;
                    position: absolute;
                    left: 0;
                    top: 80rpx;
                }

                .detail {
                    position: relative;
                    view {
                        &:first-child {
                            color: rgba(0, 0, 0, 0.54);
                            font-size: 26rpx;
                        }
                        &:last-child {
                            color: rgba(0, 0, 0, 0.4);
                            font-size: 24rpx;
                            line-height: 1.5;
                        }
                    }
                    &::before {
                        width: 16rpx;
                        height: 16rpx;
                        border-radius: 8rpx;
                        background-color: #ffe48c;
                        content: '';
                        display: block;
                        position: absolute;
                        left: -6rpx;
                        top: 25rpx;
                    }
                }
            }

            .qa-slot {
                .content {
                    width: calc(100vw - 120rpx);
                    background-color: #fff;
                    padding: 10rpx 16rpx;
                    border-radius: 32rpx;
                    margin-bottom: 16rpx;
                    box-sizing: border-box;
                }

                .item {
                    display: inline-block;
                    background: rgb(254, 242, 218);
                    padding: 10rpx 12rpx;
                    border-radius: 30rpx;
                    margin-right: 10rpx;
                    margin-bottom: 15rpx;
                    text-align: center;

                    view {
                        line-height: 1;
                        &:first-child {
                            color: rgb(51, 51, 51);
                            font-size: 24rpx;
                        }

                        &:last-child {
                            color: rgba(0, 0, 0, 0.4);
                        }
                    }
                }
            }

            .adaptable-action-card {
                width: calc(100vw - 120rpx);
                background-color: #fff;
                border-radius: 30rpx;

                image {
                    width: 100%;
                    border-radius: 30rpx 30rpx 0 0;
                }

                &-title {
                    color: rgba(0, 0, 0, 0.87);
                    font-size: 34rpx;
                    font-weight: 500;
                }

                &-content {
                    color: #333;
                    line-height: 1.3;
                    margin-top: 20rpx;
                    margin-bottom: 10rpx;
                }
                
                .transverse {
                    .default,
                    .primary {
                        text-align: center;
                        padding: 5rpx 12rpx;
                        border-radius: 30rpx;
                        margin-right: 10rpx;
                        margin-bottom: 10rpx;
                    }

                    .default {
                        color: rgba(0, 0, 0, 0.87);
                        border: 1px solid rgba(0, 0, 0, 0.1);
                    }

                    .primary {
                        color: #fff;
                        background-color: #ffb300;
                        border: 1px solid #ffb300;
                    }
                }
                
                .vertical{
                   .default,
                   .primary {
                       text-align: center;
                       padding: 10rpx 0;
                       
                       border-top: 1px solid rgba(0, 0, 0, 0.1);
                   }
                   
                   .default {
                       color: rgba(0, 0, 0, 0.87);
                   }
                   
                   .primary {
                       color: #ffb300;
                   } 
                }
            }
        }
    }

    .fixed-btn {
        width: 100%;
        height: 140rpx;
        position: fixed;
        bottom: 0;
        --window-left: 0;
        background-color: #fff;
        box-shadow: 0 0 5rpx 1rpx rgba(0, 0, 0, 0.08);
        box-sizing: border-box;

        input {
            width: 100%;
            height: 76rpx;
            line-height: 76rpx;
            border-radius: 100rpx;
            background-color: #f1f5ff;
            padding: 0 30rpx;
        }
    }
}

.collapse-popup {
    position: relative;
    padding: 40rpx;

    .close {
        position: absolute;
        top: -15rpx;
        right: 10rpx;
    }

    view {
        font-size: 26rpx;

        &:first-child {
            font-size: 28rpx;
            margin-bottom: 20rpx;
            text-align: center;
            font-weight: bold;
        }
    }
}

效果图如下

image.png

image.png

参考链接:help.aliyun.com/zh/beebot/u…