提交 1223366f 编写于 作者: DCloud_JSON's avatar DCloud_JSON

1.0.12

上级 fa34d5a7
## 1.0.12(2023-05-22)
- 优化 提升性能
- 更新 更改`uni-ai-chat`云对象的`runtime`版本为`Nodejs12`
## 1.0.11(2023-05-17) ## 1.0.11(2023-05-17)
- 修复 流式响应模式,微信小程序端,部分情况下:消息内容突然变空白的问题 - 修复 流式响应模式,微信小程序端,部分情况下:消息内容突然变空白的问题
- 优化 uni-ai问题回复完成后,消息输入框自动获取焦点 - 优化 uni-ai问题回复完成后,消息输入框自动获取焦点
......
@import "@/lib/highlight/github-dark.min.css"; @import "@/lib/highlight/github-dark.min.css";
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
.rich-text-box ::v-deep pre.hljs { .rich-text-box ::v-deep pre.hljs {
......
<template> <template>
<view class="rich-text-box" :class="{'show-cursor':showCursor}" ref="rich-text-box"> <view class="msg-item">
<rich-text v-if="nodes&&nodes.length" space="nbsp" :nodes="nodes"></rich-text> <view class="create_time-box">
<uni-dateformat class="create_time" :date="msg.create_time" format="MM/dd hh:mm:ss"></uni-dateformat>
<!-- #ifdef H5 --> </view>
<view class="copy-box" :style="{left,top}"> <view :class="{reverse:!msg.isAi}">
<text class="copy" @click="copy">复制</text> <view class="userInfo">
<!-- <view v-if="left != '-100px'" class="copy-mask" @click="left = '-100px'"></view> --> <image class="avatar" :src="msg.isAi?'../../static/uni-ai.png':'../../static/avatar.png'" mode="widthFix"></image>
</view> </view>
<!-- #endif --> <view class="content">
</view>
<view class="rich-text-box" :class="{'show-cursor':showCursor}" ref="rich-text-box">
<rich-text v-if="nodes&&nodes.length" space="nbsp" :nodes="nodes"></rich-text>
<!-- #ifdef H5 -->
<view class="copy-box" :style="{left,top}">
<text class="copy" @click="copy">复制</text>
</view>
<!-- #endif -->
</view>
<view v-if="msgIndex == msgIndexList.length-1 && adpid && msg.insufficientScore">
<uni-ad-rewarded-video :adpid="adpid" @onAdClose="onAdClose"></uni-ad-rewarded-video>
</view>
</view>
<uni-icons v-if="msgIndex == msgIndexList.length-1 && !msg.isAi && msg.state != 100 && msgStateIcon(msg)"
@click="msg.state == -100 ? retriesSendMsg() : ''" :color="msg.state===0?'#999':'#d22'"
:type="msgStateIcon(msg)" class="msgStateIcon">
</uni-icons>
</view>
</view>
</template> </template>
<script> <script>
import {msgList} from '@/pages/chat/msgList.js';
// 引入markdown-it库 // 引入markdown-it库
import MarkdownIt from '@/lib/markdown-it.min.js'; import MarkdownIt from '@/lib/markdown-it.min.js';
...@@ -53,13 +76,17 @@ ...@@ -53,13 +76,17 @@
export default { export default {
name: "msg", name: "msg",
data() { data() {
return { return {
// 悬浮的复制按钮的左边距 // 悬浮的复制按钮的左边距
left: "-100px", left: "-100px",
// 悬浮的复制按钮的上边距 // 悬浮的复制按钮的上边距
top: "-100px" top: "-100px",
msg:{
content:""
},
msgIndexList:0
}; };
}, },
mounted() { mounted() {
// #ifdef H5 // #ifdef H5
// web端限制不选中文字时出现系统右键菜单 // web端限制不选中文字时出现系统右键菜单
...@@ -86,24 +113,36 @@ ...@@ -86,24 +113,36 @@
this.left = "-100px" this.left = "-100px"
}) })
// #endif // #endif
},
created() {
this.msg = msgList[this.msgIndex]
}, },
props: { props: {
// 传入的markdown内容 // // 传入的markdown内容
md: { // md: {
type: String, // type: String,
default () { // default () {
return '' // return ''
} // }
}, // },
// 是否显示鼠标闪烁的效果 // 是否显示鼠标闪烁的效果
showCursor: { showCursor: {
type: [Boolean, Number], type: [Boolean, Number],
default () { default () {
return false return false
} }
},
msgIndex:{
type: Number,
default () {
return false
}
} }
}, },
computed: { computed: {
md(){
return this.msg.content
},
nodes() { nodes() {
let htmlString = '' let htmlString = ''
// 修改转换结果的htmlString值 用于正确给界面增加鼠标闪烁的效果 // 修改转换结果的htmlString值 用于正确给界面增加鼠标闪烁的效果
...@@ -142,8 +181,105 @@ ...@@ -142,8 +181,105 @@
} }
</script> </script>
<style lang="scss"> <style lang="scss">
/* #ifndef APP-NVUE */
view,
textarea,
button,
.page {
display: flex;
box-sizing: border-box;
}
/* #endif */
.userInfo {
flex-direction: column;
}
.msg-item {
position: relative;
width: 750rpx;
flex-direction: column;
padding: 0 15px;
padding-bottom: 15px;
}
.msgStateIcon {
position: relative;
top: 5px;
right: 5px;
align-self: center;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 2px;
}
.create_time {
font-size: 12px;
padding: 5px 0;
padding-top: 0;
color: #aaa;
text-align: center;
width: 750rpx;
/* #ifdef MP */
display: flex;
/* #endif */
justify-content: center;
}
.content {
/* #ifndef APP-NVUE */
max-width: 550rpx;
/* #endif */
background-color: #FFF;
border-radius: 5px;
padding: 12px 10px;
margin-left: 10px;
/* #ifndef APP-NVUE */
word-break: break-all;
user-select: text;
cursor: text;
/* #endif */
}
/* #ifndef APP-NVUE */
.content {
display: inline;
}
.content ::v-deep rich-text {
max-width: 550rpx;
overflow: auto;
}
/* #endif */
/* #ifdef H5 */
.content * {
display: inline;
}
/* #endif */
.reverse {
flex-direction: row-reverse;
}
.reverse .content {
margin-left: 0;
margin-right: 10px;
}
.reverse-align {
align-items: flex-end;
}
/* #ifndef VUE3 && APP-PLUS */ /* #ifndef VUE3 && APP-PLUS */
@import "@/components/uni-ai-msg/uni-ai-msg.scss"; @import "@/components/uni-ai-msg/uni-ai-msg.scss";
/* #endif */ /* #endif */
</style> </style>
\ No newline at end of file
{ {
"id": "uni-ai-chat", "id": "uni-ai-chat",
"name": "uni-ai-chat", "name": "uni-ai-chat",
"version": "1.0.11", "version": "1.0.12",
"description": "基于uni-ai的聊天示例项目,支持流式、支持前文总结,云端一体", "description": "基于uni-ai的聊天示例项目,支持流式、支持前文总结,云端一体",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
......
...@@ -2,30 +2,12 @@ ...@@ -2,30 +2,12 @@
<view class="page"> <view class="page">
<view class="container"> <view class="container">
<view v-if="isWidescreen" class="header">uni-ai-chat</view> <view v-if="isWidescreen" class="header">uni-ai-chat</view>
<text class="noData" v-if="msgList.length === 0">没有对话记录</text> <text class="noData" v-if="msgLength === 0">没有对话记录</text>
<scroll-view :scroll-into-view="scrollIntoView" scroll-y="true" class="msg-list" :enable-flex="true"> <scroll-view :scroll-into-view="scrollIntoView" scroll-y="true" class="msg-list" :enable-flex="true">
<view v-for="(msg,index) in msgList" class="msg-item" :key="index"> <uni-ai-msg ref="msg" v-for="(msgIndex,index) in msgLength" :key="index" :msgIndex="index"
<view class="create_time-box"> :show-cursor="index == msgLength - 1 && msgLength%2 === 0 && sseIndex"
<uni-dateformat class="create_time" :date="msg.create_time" format="MM/dd hh:mm:ss"></uni-dateformat> ></uni-ai-msg>
</view> <view class="tip-ai-ing" v-if="msgLength && msgLength%2 !== 0">
<view :class="{reverse:!msg.isAi}">
<view class="userInfo">
<image class="avatar" :src="msg.isAi?'../../static/uni-ai.png':'../../static/avatar.png'" mode="widthFix"></image>
</view>
<view class="content">
<!-- <text class="copy" @click="copy">复制</text> -->
<uni-ai-msg :md="msg.content" :show-cursor="index == msgList.length-1 && msg.isAi && sseIndex"></uni-ai-msg>
<view v-if="index == msgList.length-1 && adpid && msg.insufficientScore">
<uni-ad-rewarded-video :adpid="adpid" @onAdClose="onAdClose"></uni-ad-rewarded-video>
</view>
</view>
<uni-icons v-if="index == msgList.length-1 && !msg.isAi && msg.state != 100 && msgStateIcon(msg)"
@click="msg.state == -100 ? retriesSendMsg() : ''" :color="msg.state===0?'#999':'#d22'"
:type="msgStateIcon(msg)" class="msgStateIcon">
</uni-icons>
</view>
</view>
<view class="tip-ai-ing" v-if="msgList.length && msgList.length%2 !== 0">
<text>uni-ai正在思考中...</text> <text>uni-ai正在思考中...</text>
<view v-if="NODE_ENV == 'development' && !enableStream"> <view v-if="NODE_ENV == 'development' && !enableStream">
如需提速,请开通<uni-link class="uni-link" href="https://uniapp.dcloud.net.cn/uniCloud/uni-ai-chat.html" text="[流式响应]"></uni-link> 如需提速,请开通<uni-link class="uni-link" href="https://uniapp.dcloud.net.cn/uniCloud/uni-ai-chat.html" text="[流式响应]"></uni-link>
...@@ -46,24 +28,29 @@ ...@@ -46,24 +28,29 @@
<uni-icons @click="clear" type="trash" size="24" color="#888"></uni-icons> <uni-icons @click="clear" type="trash" size="24" color="#888"></uni-icons>
</view> </view>
<view class="textarea-box"> <view class="textarea-box">
<textarea v-model="content" :cursor-spacing="15" class="textarea" :auto-height="!isWidescreen" <textarea v-model="content" :cursor-spacing="15" class="textarea" :auto-height="!isWidescreen"
@keyup.shift="onKeyup('shift')" @keydown.shift="onKeydown('shift')" @keydown.enter="onKeydown('enter')" @keyup.shift="onKeyup('shift')" @keydown.shift="onKeydown('shift')"
:disabled="inputBoxDisabled" :placeholder="placeholderText" :maxlength="-1" :focus="focus" @keydown.enter="onKeydown('enter')" :disabled="inputBoxDisabled"
:placeholder="placeholderText" :maxlength="-1" :focus="focus"
placeholder-class="input-placeholder"></textarea> placeholder-class="input-placeholder"></textarea>
</view> </view>
<view class="send-btn-box"> <view class="send-btn-box">
<text v-if="isWidescreen" class="send-btn-tip">↵ 发送 / shift + ↵ 换行</text> <text v-if="isWidescreen" class="send-btn-tip">↵ 发送 / shift + ↵ 换行</text>
<button @click="beforeSendMsg" :disabled="inputBoxDisabled || !content" class="send" type="primary">发送</button> <button @click="beforeSendMsg" :disabled="inputBoxDisabled || !content" class="send"
type="primary">发送</button>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
// 引入配置文件 // 引入配置文件
import config from '@/config.js'; import config from '@/config.js';
import {msgList} from '@/pages/chat/msgList.js';
// 获取广告id // 获取广告id
const { const {
adpid adpid
...@@ -72,15 +59,17 @@ ...@@ -72,15 +59,17 @@
let sseChannel = false; let sseChannel = false;
// 是否通过回调,当用户点击清空后应当跳过前一次请求的回调 // 是否通过回调,当用户点击清空后应当跳过前一次请求的回调
let skip_callback = false; let skip_callback = false;
// 键盘的shift键是否被按下 // 键盘的shift键是否被按下
let shiftKeyPressed = false let shiftKeyPressed = false
export default { export default {
data() { data() {
return { return {
// 使聊天窗口滚动到指定元素id的值 // 使聊天窗口滚动到指定元素id的值
scrollIntoView: "", scrollIntoView: "",
// 消息长度(个数)
msgLength:0,
// 消息列表数据 // 消息列表数据
msgList: [], msgList: [],
// 输入框的消息内容 // 输入框的消息内容
...@@ -92,10 +81,10 @@ ...@@ -92,10 +81,10 @@
// 当前屏幕是否为宽屏 // 当前屏幕是否为宽屏
isWidescreen: false, isWidescreen: false,
// 广告位id // 广告位id
adpid, adpid,
focus:false focus: false
} }
}, },
computed: { computed: {
// 输入框是否禁用 // 输入框是否禁用
inputBoxDisabled() { inputBoxDisabled() {
...@@ -104,7 +93,7 @@ ...@@ -104,7 +93,7 @@
return true return true
} }
// 如果消息列表长度为奇数,则禁用输入框 // 如果消息列表长度为奇数,则禁用输入框
return !!(this.msgList.length && this.msgList.length % 2 !== 0) return !!(this.msgLength && this.msgLength % 2 !== 0)
}, },
// 输入框占位符文本 // 输入框占位符文本
placeholderText() { placeholderText() {
...@@ -125,17 +114,26 @@ ...@@ -125,17 +114,26 @@
} }
}, },
// 监听msgList变化,将其存储到本地缓存中 // 监听msgList变化,将其存储到本地缓存中
watch: { watch: {
// #ifdef H5 // #ifdef H5
inputBoxDisabled(val){ inputBoxDisabled(val) {
this.$nextTick(()=>{ this.$nextTick(() => {
this.focus = !val this.focus = !val
console.log('this.focus',this.focus); // console.log('this.focus', this.focus);
}) })
}, },
// #endif // #endif
msgList: { msgList: {
handler(msgList) { handler(msgList) {
let msgLength = msgList.length
if(msgLength != this.msgLength){
this.msgLength = msgLength
this.$nextTick(()=>{
this.updateLastMsg(msgList[msgLength - 1])
})
}
// 将msgList存储到本地缓存中 // 将msgList存储到本地缓存中
uni.setStorage({ uni.setStorage({
"key":"uni-ai-msg", "key":"uni-ai-msg",
...@@ -146,7 +144,8 @@ ...@@ -146,7 +144,8 @@
deep: true deep: true
} }
}, },
async mounted() { async mounted() {
// 如果存在广告位id且用户token未过期 // 如果存在广告位id且用户token未过期
if (this.adpid && uniCloud.getCurrentUserInfo().tokenExpired > Date.now()) { if (this.adpid && uniCloud.getCurrentUserInfo().tokenExpired > Date.now()) {
// 查询当前用户的积分 // 查询当前用户的积分
...@@ -165,7 +164,7 @@ ...@@ -165,7 +164,7 @@
.get() .get()
// 输出当前用户积分 // 输出当前用户积分
console.log('当前用户有多少积分:', res.data[0] && res.data[0].score); console.log('当前用户有多少积分:', res.data[0] && res.data[0].score);
} }
// for (let i = 0; i < 15; i++) { // for (let i = 0; i < 15; i++) {
// this.msgList.push({ // this.msgList.push({
...@@ -173,9 +172,14 @@ ...@@ -173,9 +172,14 @@
// content: "1-" + i // content: "1-" + i
// }) // })
// } // }
this.msgList = uni.getStorageSync('uni-ai-msg') || []
let _msgList = uni.getStorageSync('uni-ai-msg') || [];
if(_msgList.length){
msgList.push(..._msgList)
}
this.msgList = msgList
// 如果上一次对话中 最后一条消息ai未回复。则一启动就自动重发。 // 如果上一次对话中 最后一条消息ai未回复。则一启动就自动重发。
let length = this.msgList.length let length = this.msgList.length
...@@ -202,59 +206,59 @@ ...@@ -202,59 +206,59 @@
}, matches => { }, matches => {
this.isWidescreen = matches; this.isWidescreen = matches;
}) })
// #endif // #endif
// 兼容 Vue3下textarea不支持@keydown // 兼容 Vue3下textarea不支持@keydown
// #ifdef H5 && VUE3 // #ifdef H5 && VUE3
//获得消息输入框对象 //获得消息输入框对象
let adjunctKeydown = false let adjunctKeydown = false
const textareaDom = document.querySelector('.textarea-box textarea'); const textareaDom = document.querySelector('.textarea-box textarea');
if (textareaDom) { if (textareaDom) {
//键盘按下时 //键盘按下时
textareaDom.onkeydown = e => { textareaDom.onkeydown = e => {
// console.log('onkeydown', e.keyCode) // console.log('onkeydown', e.keyCode)
if ([16, 17, 18, 93].includes(e.keyCode)) { if ([16, 17, 18, 93].includes(e.keyCode)) {
//按下了shift ctrl alt windows键 //按下了shift ctrl alt windows键
adjunctKeydown = true; adjunctKeydown = true;
} }
if (e.keyCode == 13 && !adjunctKeydown) { if (e.keyCode == 13 && !adjunctKeydown) {
// 延迟兼容 v-model的时机小于onkeydown的问题 // 延迟兼容 v-model的时机小于onkeydown的问题
this.content = textareaDom.value this.content = textareaDom.value
// 执行发送 // 执行发送
this.beforeSendMsg(); this.beforeSendMsg();
} }
}; };
textareaDom.onkeyup = e => { textareaDom.onkeyup = e => {
//松开adjunct键 //松开adjunct键
if ([16, 17, 18, 93].includes(e.keyCode)) { if ([16, 17, 18, 93].includes(e.keyCode)) {
adjunctKeydown = false; adjunctKeydown = false;
} }
}; };
} }
// #endif // #endif
}, },
methods: { methods: {
// #ifdef H5 && VUE2 // #ifdef H5 && VUE2
onKeydown(keyname){ onKeydown(keyname) {
if(keyname == 'shift'){ if (keyname == 'shift') {
//按下了shift键 //按下了shift键
shiftKeyPressed = true; shiftKeyPressed = true;
} }
// 按下了回车 且 之前没按下 shift // 按下了回车 且 之前没按下 shift
if (keyname == 'enter' && !shiftKeyPressed) { if (keyname == 'enter' && !shiftKeyPressed) {
this.$nextTick(()=>{ this.$nextTick(() => {
this.beforeSendMsg(); this.beforeSendMsg();
}) })
} }
}, },
onKeyup(keyname){ onKeyup(keyname) {
if(keyname == 'shift'){ if (keyname == 'shift') {
//按下了shift键 //按下了shift键
shiftKeyPressed = false; shiftKeyPressed = false;
} }
}, },
// #endif // #endif
// 此(惰性)函数,检查是否开通uni-push;决定是否启用enableStream // 此(惰性)函数,检查是否开通uni-push;决定是否启用enableStream
async checkIsOpenPush() { async checkIsOpenPush() {
try { try {
...@@ -269,7 +273,7 @@ ...@@ -269,7 +273,7 @@
}, },
// 更新最后一条消息 // 更新最后一条消息
updateLastMsg(param) { updateLastMsg(param) {
let length = this.msgList.length let length = this.msgLength
if (length === 0) { if (length === 0) {
return return
} }
...@@ -288,7 +292,7 @@ ...@@ -288,7 +292,7 @@
lastMsg = Object.assign(lastMsg, data) lastMsg = Object.assign(lastMsg, data)
} }
} }
this.msgList.splice(length - 1, 1, lastMsg) this.msgList.splice(length - 1, 1, lastMsg)
}, },
// 广告关闭事件 // 广告关闭事件
onAdClose(e) { onAdClose(e) {
...@@ -420,13 +424,13 @@ ...@@ -420,13 +424,13 @@
state: 0, state: 0,
// 消息创建时间 // 消息创建时间
create_time: Date.now() create_time: Date.now()
}) })
// 展示最后一条消息 // 展示最后一条消息
this.showLastMsg() this.showLastMsg()
// dom加载完成后 清空文本内容 // dom加载完成后 清空文本内容
this.$nextTick(() => { this.$nextTick(() => {
this.content = '' this.content = ''
}) })
this.send() // 发送消息 this.send() // 发送消息
}, },
...@@ -476,7 +480,7 @@ ...@@ -476,7 +480,7 @@
// console.log('sseChannel',sseChannel); // console.log('sseChannel',sseChannel);
// 监听message事件 // 监听message事件
sseChannel.on('message',(message) => { sseChannel.on('message', (message) => {
// console.log('on message', message); // console.log('on message', message);
// 将从云端接收到的消息添加到消息列表中 // 将从云端接收到的消息添加到消息列表中
...@@ -505,20 +509,20 @@ ...@@ -505,20 +509,20 @@
if (e) { if (e) {
// 更新最后一条消息 // 更新最后一条消息
this.updateLastMsg(lastMsg => { this.updateLastMsg(lastMsg => {
// 如果e包含illegal属性 // 如果e包含illegal属性
if (e.illegal) { if (e.illegal) {
// 将最后一条消息的illegal属性更新为e的illegal属性 // 将最后一条消息的illegal属性更新为e的illegal属性
lastMsg.illegal = e.illegal lastMsg.illegal = e.illegal
lastMsg.content = "内容涉及敏感" lastMsg.content = "内容涉及敏感"
// 倒数第二条(用户发问内容)也需要设置illegal的值 // 倒数第二条(用户发问内容)也需要设置illegal的值
this.msgList[this.msgList.length - 2].illegal = e.illegal this.msgList[this.msgList.length - 2].illegal = e.illegal
} }
// 如果e包含summarize属性 // 如果e包含summarize属性
else if (e.summarize) { else if (e.summarize) {
// 将最后一条消息的summarize属性更新为e的summarize属性 // 将最后一条消息的summarize属性更新为e的summarize属性
lastMsg.summarize = e.summarize lastMsg.summarize = e.summarize
} }
// 如果e包含insufficientScore属性 // 如果e包含insufficientScore属性
else if (e.insufficientScore) { else if (e.insufficientScore) {
// 将最后一条消息的insufficientScore属性更新为e的insufficientScore属性 // 将最后一条消息的insufficientScore属性更新为e的insufficientScore属性
lastMsg.insufficientScore lastMsg.insufficientScore
...@@ -548,51 +552,63 @@ ...@@ -548,51 +552,63 @@
}) })
.then(res => { .then(res => {
// console.log(111,res); // console.log(111,res);
if (!sseChannel && res.data) { if (!sseChannel && res.data) {
// 更新最后一条消息的状态为100(发送成功) // 更新最后一条消息的状态为100(发送成功)
this.updateLastMsg({ this.updateLastMsg({
state: 100 state: 100
}) })
// console.log(res, res.reply); // console.log(res, res.reply);
// 判断是否要跳过本次回调,防止请求未返回时,历史对话已被清空。引起对话顺序错误 导致 对话输入框卡住 // 判断是否要跳过本次回调,防止请求未返回时,历史对话已被清空。引起对话顺序错误 导致 对话输入框卡住
if (!skip_callback) { if (!skip_callback) {
let {"reply": content,summarize,insufficientScore,illegal} = res.data let {
"reply": content,
summarize,
insufficientScore,
illegal
} = res.data
if (illegal) { if (illegal) {
// 如果返回的数据包含illegal属性,就更新最后一条消息的illegal属性为true // 如果返回的数据包含illegal属性,就更新最后一条消息的illegal属性为true
this.updateLastMsg({illegal: true}) this.updateLastMsg({
illegal: true
})
} }
// 将从云端接收到的消息添加到消息列表中 // 将从云端接收到的消息添加到消息列表中
this.msgList.push({ this.msgList.push({
// 添加消息创建时间 // 添加消息创建时间
create_time: Date.now(), create_time: Date.now(),
// 标记消息为来自AI机器人 // 标记消息为来自AI机器人
isAi: true, isAi: true,
// 添加消息内容 // 添加消息内容
content, content,
// 添加消息总结 // 添加消息总结
summarize, summarize,
// 添加消息分数不足标记 // 添加消息分数不足标记
insufficientScore, insufficientScore,
// 添加消息涉敏标记 // 添加消息涉敏标记
illegal illegal
}) })
// 滚动窗口以显示最新的一条消息 // 滚动窗口以显示最新的一条消息
this.$nextTick(()=>{ this.$nextTick(() => {
this.showLastMsg() this.showLastMsg()
}) })
} else { } else {
console.log('用户点击了清空按钮,跳过前一次请求的回调。内容:', res.data.reply); console.log('用户点击了清空按钮,跳过前一次请求的回调。内容:', res.data.reply);
} }
}else{
// 处理 sseChannel没结束 云函数提前结束的情况
sseChannel.close()
this.sseIndex = 0
} }
}) })
.catch(e => { .catch(e => {
console.log(e); console.log(e);
// 获取消息列表长度 // 获取消息列表长度
let l = this.msgList.length let l = this.msgList.length
// console.log(l,this.msgList[l-1]); // console.log(l,this.msgList[l-1]);
// 如果最后一条消息的来源是人工智能机器人 就将流式响应计数值设置为0 // 如果最后一条消息的来源是人工智能机器人 就将流式响应计数值设置为0
if (l && sseChannel && this.msgList[l - 1].isAi) { if (l && sseChannel && this.msgList[l - 1].isAi) {
sseChannel.close()
this.sseIndex = 0 this.sseIndex = 0
} }
...@@ -609,14 +625,14 @@ ...@@ -609,14 +625,14 @@
}, },
// 滚动窗口以显示最新的一条消息 // 滚动窗口以显示最新的一条消息
showLastMsg() { showLastMsg() {
// 等待DOM更新 // 等待DOM更新
this.$nextTick(() => { this.$nextTick(() => {
// 将scrollIntoView属性设置为"last-msg-item",以便滚动窗口到最后一条消息 // 将scrollIntoView属性设置为"last-msg-item",以便滚动窗口到最后一条消息
this.scrollIntoView = "last-msg-item" this.scrollIntoView = "last-msg-item"
// 等待DOM更新,即:滚动完成 // 等待DOM更新,即:滚动完成
this.$nextTick(() => { this.$nextTick(() => {
// 将scrollIntoView属性设置为空,以便下次设置滚动条位置可被监听 // 将scrollIntoView属性设置为空,以便下次设置滚动条位置可被监听
this.scrollIntoView = "" this.scrollIntoView = ""
}) })
}) })
}, },
...@@ -659,7 +675,7 @@ ...@@ -659,7 +675,7 @@
// 将流式响应计数值归零 // 将流式响应计数值归零
this.sseIndex = 0 this.sseIndex = 0
// 将消息列表清空 // 将消息列表清空
this.msgList = [] this.msgList.splice(0,this.msgLength);
} }
} }
}); });
...@@ -739,7 +755,7 @@ ...@@ -739,7 +755,7 @@
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
overflow: auto; overflow: auto;
/* #endif */ /* #endif */
width: 450rpx; width: 450rpx;
font-size: 14px; font-size: 14px;
} }
...@@ -748,10 +764,11 @@ ...@@ -748,10 +764,11 @@
.textarea-box .textarea::-webkit-scrollbar { .textarea-box .textarea::-webkit-scrollbar {
width: 0; width: 0;
} }
/* #endif */ /* #endif */
.input-placeholder { .input-placeholder {
color: #bbb; color: #bbb;
line-height: 18px; line-height: 18px;
} }
...@@ -783,7 +800,6 @@ ...@@ -783,7 +800,6 @@
.send::after { .send::after {
display: none; display: none;
} }
/* #endif */ /* #endif */
...@@ -792,92 +808,6 @@ ...@@ -792,92 +808,6 @@
height: 1px; height: 1px;
width: 750rpx; width: 750rpx;
} }
.userInfo {
flex-direction: column;
}
.msg-item {
position: relative;
width: 750rpx;
flex-direction: column;
padding: 0 15px;
padding-bottom: 15px;
}
.msgStateIcon {
position: relative;
top: 5px;
right: 5px;
align-self: center;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 2px;
}
.create_time {
font-size: 12px;
padding: 5px 0;
padding-top: 0;
color: #aaa;
text-align: center;
width: 750rpx;
/* #ifdef MP */
display: flex;
/* #endif */
justify-content: center;
}
.content {
/* #ifndef APP-NVUE */
max-width: 550rpx;
/* #endif */
background-color: #FFF;
border-radius: 5px;
padding: 12px 10px;
margin-left: 10px;
/* #ifndef APP-NVUE */
word-break: break-all;
user-select: text;
cursor: text;
/* #endif */
}
/* #ifndef APP-NVUE */
.content {
display: inline;
}
.content ::v-deep rich-text {
max-width: 550rpx;
overflow: auto;
}
/* #endif */
/* #ifdef H5 */
.content * {
display: inline;
}
/* #endif */
.reverse {
flex-direction: row-reverse;
}
.reverse .content {
margin-left: 0;
margin-right: 10px;
}
.reverse-align {
align-items: flex-end;
}
.noData { .noData {
margin-top: 15px; margin-top: 15px;
text-align: center; text-align: center;
...@@ -886,7 +816,6 @@ ...@@ -886,7 +816,6 @@
font-size: 12px; font-size: 12px;
justify-content: center; justify-content: center;
} }
.tip-ai-ing { .tip-ai-ing {
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
......
export const msgList = []
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
"uni-cloud-ai": {} "uni-cloud-ai": {}
}, },
"cloudfunction-config": { "cloudfunction-config": {
"timeout": 60 "timeout": 60,
"runtime":"Nodejs12"
} }
} }
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册