Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DCloud
uni-ai-chat
提交
e89415cc
U
uni-ai-chat
项目概览
DCloud
/
uni-ai-chat
通知
893
Star
11
Fork
8
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
2
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
U
uni-ai-chat
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
2
Issue
2
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
e89415cc
编写于
5月 22, 2023
作者:
DCloud_JSON
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
1.0.12
上级
d6d134d1
变更
7
隐藏空白更改
内联
并排
Showing
7 changed file
with
341 addition
and
271 deletion
+341
-271
changelog.md
changelog.md
+3
-0
components/uni-ai-msg/uni-ai-msg.scss
components/uni-ai-msg/uni-ai-msg.scss
+1
-1
components/uni-ai-msg/uni-ai-msg.vue
components/uni-ai-msg/uni-ai-msg.vue
+161
-25
package.json
package.json
+1
-1
pages/chat/chat.vue
pages/chat/chat.vue
+172
-243
pages/chat/msgList.js
pages/chat/msgList.js
+1
-0
uniCloud-aliyun/cloudfunctions/uni-ai-chat/package.json
uniCloud-aliyun/cloudfunctions/uni-ai-chat/package.json
+2
-1
未找到文件。
changelog.md
浏览文件 @
e89415cc
## 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问题回复完成后,消息输入框自动获取焦点
...
...
components/uni-ai-msg/uni-ai-msg.scss
浏览文件 @
e89415cc
@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
{
...
...
components/uni-ai-msg/uni-ai-msg.vue
浏览文件 @
e89415cc
<
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
package.json
浏览文件 @
e89415cc
{
{
"id"
:
"uni-ai-chat"
,
"id"
:
"uni-ai-chat"
,
"name"
:
"uni-ai-chat"
,
"name"
:
"uni-ai-chat"
,
"version"
:
"1.0.1
1
"
,
"version"
:
"1.0.1
2
"
,
"description"
:
"基于uni-ai的聊天示例项目,支持流式、支持前文总结,云端一体"
,
"description"
:
"基于uni-ai的聊天示例项目,支持流式、支持前文总结,云端一体"
,
"main"
:
"main.js"
,
"main"
:
"main.js"
,
"scripts"
:
{
"scripts"
:
{
...
...
pages/chat/chat.vue
浏览文件 @
e89415cc
...
@@ -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=
"msgL
ist.l
ength === 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
.
msgL
ist
.
length
&&
this
.
msgList
.
l
ength
%
2
!==
0
)
return
!!
(
this
.
msgL
ength
&&
this
.
msgL
ength
%
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
.
msgL
ist
.
l
ength
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
;
...
...
pages/chat/msgList.js
0 → 100644
浏览文件 @
e89415cc
export
const
msgList
=
[]
uniCloud-aliyun/cloudfunctions/uni-ai-chat/package.json
浏览文件 @
e89415cc
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录