Files
xdd-uniapp-new/aiChat/views/index.vue
T

592 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view
:class="scrollTop > 0 ? 'page-scroll' : ''"
class="fix-tabbar-page"
>
<hx-navbar
:back="false"
:fixed="true"
:statusBar="true"
:pageScroll.sync="scrollData"
left-icon="arrowleft"
color="#333"
transparent="auto"
barPlaceholder="hidden"
title="云灵AI智能助手"
@click-left="clickBackHandle"
/>
<page-container
:show="true"
:overlay="false"
:z-index="1"
@beforeleave="pageContainerBeforeleave"
>
<view style="opacity: 0;">
遮罩层用于监听实体键返回
</view>
</page-container>
<view
id="fixTbabarBody"
:style="{
'padding-top': (44 + statusBarHeight) + 'px'
}"
class="fix-tabbar-body"
>
<block v-if="chatMessageListLength < 1">
<view class="home-focus-wrap">
<view class="focus-guide-wrap">
<view class="guide-left">
<image
:src="webUrl + '/aiChat/logo-01.gif'"
class="avatar"
mode="widthFix"
/>
</view>
<view class="guide-right">
<view class="trangle" />
<view class="title">{{ settingInfo.helloMessageTitle }}</view>
<view class="content">{{ settingInfo.helloMessageContent }}</view>
</view>
</view>
</view>
<view class="topic-wrap">
<view class="title">
<image
:src="webUrl + '/aiChat/icon-01.png'"
class="icon"
mode="widthFix"
/>
<view class="txt">#大家都在问</view>
<image
:src="webUrl + '/aiChat/icon-01.png'"
class="icon"
mode="widthFix"
/>
</view>
<view class="list-wrap">
<view
:style="{
'width': animationWidth,
'animation': animationStr
}"
class="list-cont"
>
<view
v-for="(item, index) in topicList"
:key="index"
class="list-item"
@click="topicItemClickHandle(item.title)"
>
<image
v-if="item.isHot"
:src="webUrl + '/aiChat/hot.png'"
class="icon"
mode="widthFix"
/>
{{ item.title }}
</view>
</view>
</view>
</view>
</block>
<view class="chat-wrap">
<view v-if="chatMessageListLength > 0" class="chat-list-wrap">
<view
v-for="(item, index) in chatMessageList"
:key="index"
class="chat-message-item"
>
<view v-if="item.type === -1" class="chat-message-body">
<view class="item-right">
<view class="txt">
{{ item.prompt }}
</view>
</view>
</view>
<view v-if="item.type === -2" class="chat-message-body">
<view class="avatar">
<image
:src="webUrl + '/aiChat/avatar.png'"
class="chat-avatar"
mode="widthFix"
/>
</view>
<view class="item-left">
<view class="txt">
<view
ref="rich-text-box"
:class="{
'show-cursor': item.showCursor
}"
class="rich-text-box"
>
<rich-text
v-if="item.nodes && item.nodes.length"
:nodes="item.nodes"
space="nbsp"
/>
<view v-if="!(item.nodes && item.nodes.length) && loading" class="loader-wrap">
<div class="loading2" />
</view>
</view>
<view
v-if="!item.showCursor && item.nodes && item.nodes.length && !item.isError"
class="bottom-btns"
>
<!-- <button
open-type="share"
plain="true"
class="item"
@click="shareHandle"
>
<image
:src="webUrl + '/icon-25.png'"
class="icon"
mode="widthFix"
/>
分享
</button> -->
<view class="item" @click="copyContentHandle(item.content)">
<image
:src="webUrl + '/aiChat/icon-02.png'"
class="icon"
mode="widthFix"
/>
复制
</view>
<view
v-if="index === (chatMessageListLength - 1)"
class="item"
@click="regenerateHandle(item)"
>
<image
:src="webUrl + '/aiChat/icon-04.png'"
class="icon"
mode="widthFix"
/>
重新生成
</view>
<view class="item item2" @click="copyMoreInfoHandle(item)">拷贝</view>
</view>
</view>
</view>
<!-- 换一换 -->
<view
v-if="item.showMoreInfo.show"
class="change-list-wrap"
>
<view v-if="item.showMoreInfo.showType === '特产商品'" class="list-wrap">
<view
v-for="(productItem, productIndex) in item.showMoreInfo.resultItems"
:key="productIndex"
class="product-item"
@click="productItemClickHandle(productItem)"
>
<image
:src="productItem.img"
class="img"
/>
<view class="product-info">
<view class="name one-t">
{{ productItem.name }}
</view>
<view class="price-wrap">
<view class="price">
<text class="txt"></text>{{ productItem.price }}
</view>
<view class="btn">立即查看</view>
</view>
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '店铺'" class="list-wrap">
<view
v-for="(hotel, hotelIndex) in item.showMoreInfo.resultItems"
:key="hotelIndex"
class="product-item"
@click="hotelItemClickHandle(hotel)"
>
<view class="hotel-type">
{{ item.hotelTypeName }}
</view>
<image
:src="hotel.img"
class="img"
/>
<view class="product-info">
<view class="name one-t">
{{ hotel.name }}
</view>
<view class="desc-wrap one-t">
{{ hotel.description || '' }}
</view>
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '非遗文化'" class="list-wrap">
<view
v-for="(ich, ichIndex) in item.showMoreInfo.resultItems"
:key="ichIndex"
class="product-item"
@click="ichItemClickHandle(ich)"
>
<image
:src="ich.img"
class="img"
/>
<view class="product-info">
<view class="name one-t">
{{ ich.name }}
</view>
<view class="desc-wrap one-t">
{{ ich.description || '' }}
</view>
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '千县名品'" class="list-wrap">
<view
v-for="(qianxian, qianxianIndex) in item.showMoreInfo.resultItems"
:key="qianxianIndex"
class="product-item"
@click="qianxianItemClickHandle(qianxian)"
>
<image
:src="qianxian.img"
class="img"
/>
<view class="product-info">
<view class="name name2 one-t">
{{ qianxian.name }}
</view>
</view>
</view>
</view>
<view v-if="item.showMoreInfo.showType === '优惠活动'" class="list-wrap prize-list-wrap">
<view
v-for="(prize, prizeIndex) in item.showMoreInfo.resultItems"
:key="prizeIndex"
class="prize-item"
@click="prizeItemClickHandle(prize)"
>
<image
:src="prize.img"
class="img"
mode="widthFix"
/>
</view>
</view>
<view v-if="item.showMoreInfo.showType !== '优惠活动'" class="change-btn">
<image
:src="webUrl + '/aiChat/icon-change.png'"
class="icon"
mode="widthFix"
@click="analyzeKeywordsChangeHandle(item.conversationId, item.showMoreInfo)"
/>
</view>
</view>
<view
v-if="index === (chatMessageListLength - 1) && item.listQuestion.length > 0"
class="suggestion-wrap"
>
<view class="suggestion-list">
<view
v-for="(question, questionIndex) in item.listQuestion"
:key="questionIndex"
class="item"
@click="suggestionItemClickHandle(question)"
>
<view class="item-cont">
<view class="txt">
{{ question }}
</view>
<image
:src="webUrl + '/aiChat/icon-03.png'"
class="pic"
mode="widthFix"
/>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view id="bottom_postion" class="bottom-postion" />
</view>
</view>
<view
:class="{
'focus': onFocus
}"
class="page-to-bottom"
>
<view
v-if="chatNumber"
class="new-chat"
@click="createNewHandle"
>
<image
:src="webUrl + '/aiChat/icon-05.png'"
class="btn-icon"
mode="widthFix"
/>
开启新对话
</view>
<image
v-if="chatMessageListLength > 0 && !isScrollToBottom && !loading"
:src="webUrl + '/aiChat/down.png'"
class="icon"
mode="widthFix"
@click="scrollToBottomHandle"
/>
</view>
<bottom-send
ref="bottomSend"
:loading="loading"
:keyboard-height="keyboardHeight"
@send="streamReq"
@add="createNewHandle"
@history="historyViewHandle"
@focus="inputFocusHandle"
/>
<AiHistoryList
ref="historyView"
:status-bar-height="statusBarHeight"
/>
</view>
</template>
<script>
import { chatMixins } from '../mixins/chatMixins.js'
import {
getAiSystemInfoSetting,
getAiSystemInfoTopic
} from '@/api/public'
import AiHistoryList from '../components/historyList'
import BottomSend from '../components/bottomSend.vue'
export default {
name: 'AiChatIndexPage',
components: {
AiHistoryList,
BottomSend
},
mixins: [chatMixins],
data() {
return {
animationWidth: '150%',
animationStr: '',
settingInfo: {},
topicList: []
}
},
onShow() {
const addNewChat = uni.getStorageSync('addNewChat') === '1'
// 从历史界面点击新增对话
if (addNewChat && this.chatNumber) {
console.log(addNewChat)
uni.removeStorageSync('addNewChat')
this.createNewHandle()
}
},
onUnload() {
this.closeWsFn()
},
methods: {
createdCallbak() {
this.$nextTick(() => {
this.scrollToBottomHandle()
getAiSystemInfoSetting().then(res => {
const { success, data } = res
if (success) {
this.settingInfo = data
this.animationWidth = `${data.systemTopicDivMaxWidth * 750}rpx`
this.animationStr = `TranslateXSwiper-${data.systemTopicDivMaxWidth * 10} ${data.systemTopicDivScrollDuration}s infinite linear alternate`
}
})
getAiSystemInfoTopic().then(res => {
const { success, data } = res
if (success) {
this.topicList = data || []
}
})
})
},
// 创建新会话
createNewHandle() {
if (!this.chatNumber) return
this.closeWsFn()
this.chatNumber = ''
this.showCursor = false
this.loading = false
this.getConfigLoading = false
this.chatMessageList = []
},
clickBackHandle() {
if (!this.chatNumber) {
if (getCurrentPages().length > 1) {
uni.navigateBack()
} else {
uni.reLaunch({
url: '/pages/home/index'
})
}
} else {
this.createNewHandle()
}
},
pageContainerBeforeleave() {
if (!this.chatNumber) {
if (getCurrentPages().length > 1) {
uni.navigateBack()
} else {
uni.reLaunch({
url: '/pages/home/index'
})
}
} else {
this.createNewHandle()
}
}
}
}
</script>
<style lang="scss" scoped>
.chat-wrap {
padding: 0 0 180rpx 0;
.chat-info-wrap {
padding: 24rpx;
.chat-list-wrap2 {
padding: 0;
}
}
}
.loading-wrap {
text-align: center;
color: #666;
line-height: 120rpx;
}
.rich-text-box {
max-width: 100%;
}
.show-cursor .cursor {
display: inline-block;
color: #3D4CF1;
font-weight: bold;
animation: blinking 1s infinite;
}
@keyframes blinking {
from {
opacity: 1.0;
}
to {
opacity: 0.0;
}
}
.focus-guide-wrap {
display: flex;
align-items: center;
justify-content: space-between;
padding: 60rpx 40rpx;
.guide-left {
.avatar {
display: block;
width: 267rpx;
height: 530rpx;
}
}
.guide-right {
position: relative;
padding: 30rpx;
margin: 0 0 0 40rpx;
border-radius: 20rpx;
font-size: 28rpx;
background-color: #fff;
box-shadow: 0rpx 12rpx 30rpx rgba(0,0,0,0.1);
.trangle {
position: absolute;
bottom: 120rpx;
left: -15rpx;
width: 30rpx;
height: 30rpx;
background-color: #fff;
box-shadow: -30rpx 12rpx 30rpx rgba(0,0,0,0.1);
transform: rotate(45deg);
}
.title {
width: 110%;
font-weight: bold;
}
.content {
padding: 30rpx 0 0 0;
color: #999;
}
}
}
.topic-wrap {
width: 100%;
.title {
display: flex;
align-items: center;
justify-content: center;
.txt {
width: 186rpx;
height: 46rpx;
margin: 0 20rpx;
border-radius: 24rpx;
text-align: center;
line-height: 46rpx;
color: #fff;
font-size: 28rpx;
background-color: #3D4CF1;
}
.icon {
display: flex;
width: 40rpx;
height: 32rpx;
}
}
.list-wrap {
width: 100%;
margin: 60rpx 0 0 0;
overflow-x: hidden;
.list-cont {
display: flex;
flex-wrap: wrap;
width: 750 * 1.5rpx;
padding: 0 0 0 24rpx;
box-sizing: border-box;
// animation: translateXSwiper 10s infinite linear alternate;
.list-item {
display: flex;
align-items: center;
height: 48rpx;
padding: 0 18rpx;
margin: 0 30rpx 30rpx 0;
border-radius: 24rpx;
font-size: 28rpx;
color: #666;
background: rgba(255,255,255,0.39);
box-shadow: 0rpx 6rpx 12rpx rgba(0,0,0,0.1);
.icon {
display: block;
width: 24rpx;
height: 24rpx;
margin: 0 8rpx 0 0;
}
}
}
}
}
@keyframes translateXSwiper {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
</style>