提交 26aa2ef8 编写于 作者: DCloud_JSON's avatar DCloud_JSON

1.0.19

上级 a2fb542e
## 1.0.19(2023-06-05)
- 新增 支持停止响应(终止接收ai返回的答案)
- 新增 支持一键换答案(让ai重新回答一遍,得到新的答案)
- 新增 支持一键复制答案全文
- 优化 为提升用户体验;在收到ai的回答之前,不可编辑并发送新问题 改成 可以先编辑但不能发送
- 修复 由`1.0.12`重构部分逻辑引起的,发送失败后点击重复报`this.retriesSendMsg is not a function"`的问题
## 1.0.18(2023-06-02)
- 新增 支持一键复制代码块
- 修复 部分情况下偶发,ai回复的内容会串到用户的提问内容中的问题
......
......@@ -10,12 +10,6 @@
<view class="content">
<view class="rich-text-box" :class="{'show-cursor':showCursor}" ref="rich-text-box">
<rich-text v-if="nodes&&nodes.length" space="nbsp" :nodes="nodes" @itemclick="trOnclick"></rich-text>
<!-- #ifdef H5 -->
<view class="copy-box" :style="{left,top}">
<text class="copy" @click="copy">复制</text>
</view>
<!-- #endif -->
</view>
<view v-if="isLastMsg && adpid && msg.insufficientScore">
<text style="color: red;">
......@@ -24,6 +18,13 @@
</text>
<!-- <uni-ad-rewarded-video :adpid="adpid" @onAdClose="onAdClose"></uni-ad-rewarded-video> -->
</view>
<view v-if="msg.isAi" class="controller">
<text v-if="isLastMsg" title="换一个答案" @click="changeAnswer" class="retry-icon"></text>
<view @click="copy" title="复制" class="copy-icon">
<view class="copy-icon-a"></view>
<view class="copy-icon-b"></view>
</view>
</view>
</view>
<uni-icons v-if="isLastMsg && !msg.isAi && msg.state != 100 && msgStateIcon(msg)"
@click="msg.state == -100 ? retriesSendMsg() : ''" :color="msg.state===0?'#999':'#d22'"
......@@ -119,31 +120,6 @@
};
},
mounted() {
// #ifdef H5
// web端限制不选中文字时出现系统右键菜单
let richTextBox = this.$refs['rich-text-box']
if (richTextBox) {
// 监听鼠标右键事件
richTextBox.$el.addEventListener('contextmenu', (e) => {
// 判断是否选中了文字内容,如果没有则限制系统默认行为(禁止弹出右键菜单)
if (!document.getSelection().toString()) {
// console.log(e);
// 设置悬浮的复制按钮的坐标值
this.top = e.y + 'px'
this.left = e.x + 'px'
// console.log(e.x);
// console.log(e.y);
// 禁止系统默认行为(禁止弹出右键菜单)
e.preventDefault()
}
})
}
// 监听全局点击事件,隐藏悬浮的复制按钮的坐标
document.addEventListener('click', () => {
this.left = "-100px"
})
// #endif
},
created() {
this.msg = msgList[this.msgIndex]
......@@ -201,7 +177,7 @@
switch (msg.state) {
case 0:
// 发送中
return 'spinner-cycle'
return ''
break;
case -100:
// 发送失败
......@@ -236,12 +212,24 @@
})
}
},
changeAnswer(){
this.$emit('changeAnswer')
},
retriesSendMsg(){
this.$emit('retriesSendMsg')
},
// #ifdef H5
// 复制文本内容到系统剪切板
copy() {
uni.setClipboardData({
data: this.md,
showToast: false,
success() {
uni.showToast({
title: '复制成功',
icon: 'none'
});
}
})
// 设置悬浮的复制按钮的坐标值,使其隐藏
this.left = "-100px"
......@@ -302,8 +290,9 @@
}
.content {
position: relative;
/* #ifndef APP-NVUE */
max-width: 85%;
max-width: calc(85% - 15px);
/* #endif */
background-color: #FFF;
border-radius: 5px;
......@@ -316,6 +305,56 @@
/* #endif */
}
.controller {
position: absolute;
right: -25px;
bottom: 0;
width: 20px;
flex-direction: column;
height: 40px;
justify-content: flex-end;
}
.retry-icon {
font-size: 26px;
color: #d4d4d4;
height: 25px;
line-height: 15px;
margin-bottom: 5px;
}
.retry-icon:hover {
color: #BBB;
}
.retry-icon,.copy-icon {
cursor: pointer;
}
.copy-icon {
position: relative;
height: 25px;
}
.copy-icon-a,
.copy-icon-b {
position: absolute;
border: 1.5px solid #d4d4d4;
width: 10px;
height: 12px;
background-color: #FFF;
left: 2px;
top: 2px;
border-radius: 3px;
}
.copy-icon:hover .copy-icon-a,
.copy-icon:hover .copy-icon-b,{
border-color:#bbb;
}
.copy-icon-b {
top: 5px;
left: 5px;
}
/* #ifndef APP-NVUE */
.content ::v-deep rich-text {
max-width: 100%;
......
{
"id": "uni-ai-chat",
"name": "uni-ai-chat",
"version": "1.0.18",
"version": "1.0.19",
"description": "基于uni-ai的聊天示例项目,支持流式、支持前文总结,云端一体",
"main": "main.js",
"scripts": {
......
......@@ -4,7 +4,7 @@
<view v-if="isWidescreen" class="header">uni-ai-chat</view>
<text class="noData" v-if="msgLength === 0">没有对话记录</text>
<scroll-view :scroll-into-view="scrollIntoView" scroll-y="true" class="msg-list" :enable-flex="true">
<uni-ai-msg ref="msg" v-for="(msgIndex,index) in msgLength" :key="index" :msgIndex="index"
<uni-ai-msg ref="msg" v-for="(msgIndex,index) in msgLength" :key="index" :msgIndex="index" @retriesSendMsg="retriesSendMsg" @changeAnswer="changeAnswer"
:show-cursor="index == msgLength - 1 && msgLength%2 === 0 && sseIndex" :isLastMsg="index == msgLength - 1"></uni-ai-msg>
<view class="tip-ai-ing" v-if="msgLength && msgLength%2 !== 0">
<text>uni-ai正在思考中...</text>
......@@ -13,7 +13,8 @@
text="[流式响应]"></uni-link>
</view>
</view>
<view id="last-msg-item"></view>
<view @click="closeSseChannel" class="stop-responding" v-if="enableStream && sseIndex"> ▣ 停止响应</view>
<view id="last-msg-item" style="height: 1px;"></view>
</scroll-view>
<view class="foot-box">
......@@ -29,10 +30,10 @@
</view>
<view class="textarea-box" @click="focus = true">
<textarea v-model="content" :cursor-spacing="15" class="textarea" :auto-height="!isWidescreen"
:disabled="inputBoxDisabled" :placeholder="placeholderText" :maxlength="-1" :focus="focus" @blur="focus = false"
:placeholder="placeholderText" :maxlength="-1" :focus="focus" @blur="focus = false"
placeholder-class="input-placeholder"></textarea>
</view>
<view class="send-btn-box">
<view class="send-btn-box" :title="(msgLength && msgLength%2 !== 0) ? 'ai正在回复中不能发送':''">
<text v-if="isWidescreen" class="send-btn-tip">↵ 发送 / shift + ↵ 换行</text>
<button @click="beforeSendMsg" :disabled="inputBoxDisabled || !content" class="send"
type="primary">发送</button>
......@@ -97,16 +98,11 @@
},
// 输入框占位符文本
placeholderText() {
// 如果输入框被禁用,则显示“uni-ai正在回复中”
if (this.inputBoxDisabled) {
return 'uni-ai正在回复中'
} else {
// #ifdef H5
// 如果屏幕宽度大于960,则显示“请输入内容,ctrl + enter 发送”,否则显示“请输入要发给uni-ai的内容”
return window.innerWidth > 960 ? '请输入内容,ctrl + enter 发送' : '请输入要发给uni-ai的内容'
// #endif
return '请输入要发给uni-ai的内容'
}
},
// 获取当前环境
NODE_ENV() {
......@@ -328,7 +324,22 @@
// 发送消息
this.send()
},
async changeAnswer(){
this.msgList.pop()
// 防止 偶发答案涉及敏感,重复回答时。提问内容 被卡掉无法重新问
this.updateLastMsg({
illegal:false
})
this.send()
},
async beforeSendMsg() {
if(this.inputBoxDisabled){
return uni.showToast({
title: 'ai正在回复中不能发送',
icon: 'none'
});
}
// 如果开启了广告位需要登录
if (this.adpid) {
// 获取本地缓存的token
......@@ -604,6 +615,16 @@
});
})
},
closeSseChannel(){
if(sseChannel){
sseChannel.close()
sseChannel = false
}
// 将skip_callback设置为true,以便下一次请求可以正常回调
skip_callback = true
// 将流式响应计数值归零
this.sseIndex = 0
},
// 滚动窗口以显示最新的一条消息
showLastMsg() {
// 等待DOM更新
......@@ -660,6 +681,26 @@
/* #endif */
.stop-responding {
font-size: 14px;
border-radius: 3px;
margin-bottom:15px;
background-color: #f0a020;
color: #FFF;
width: 90px;
height: 30px;
line-height: 30px;
margin:0 auto;
justify-content: center;
margin-bottom: 15px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.stop-responding:hover{
box-shadow: 0 0 10px #aaa;
}
/* #ifndef APP-NVUE */
page,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册