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)
-
修复 流式响应模式,微信小程序端,部分情况下:消息内容突然变空白的问题
-
优化 uni-ai问题回复完成后,消息输入框自动获取焦点
...
...
components/uni-ai-msg/uni-ai-msg.scss
浏览文件 @
e89415cc
components/uni-ai-msg/uni-ai-msg.vue
浏览文件 @
e89415cc
<
template
>
<view
class=
"msg-item"
>
<view
class=
"create_time-box"
>
<uni-dateformat
class=
"create_time"
:date=
"msg.create_time"
format=
"MM/dd hh:mm:ss"
></uni-dateformat>
</view>
<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"
>
<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
v-if=
"left != '-100px'"
class=
"copy-mask"
@
click=
"left = '-100px'"
></view>
-->
</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
>
<
script
>
import
{
msgList
}
from
'
@/pages/chat/msgList.js
'
;
// 引入markdown-it库
import
MarkdownIt
from
'
@/lib/markdown-it.min.js
'
;
...
...
@@ -57,7 +80,11 @@
// 悬浮的复制按钮的左边距
left: "-100px",
// 悬浮的复制按钮的上边距
top: "-100px"
top: "-100px",
msg:{
content:""
},
msgIndexList:0
};
},
mounted() {
...
...
@@ -87,23 +114,35 @@
})
// #endif
},
props: {
// 传入的markdown内容
md: {
type: String,
default () {
return
''
}
created() {
this.msg = msgList[this.msgIndex]
},
props: {
// // 传入的markdown内容
// md: {
// type: String,
// default () {
// return
''
// }
// },
// 是否显示鼠标闪烁的效果
showCursor: {
type: [Boolean, Number],
default () {
return false
}
},
msgIndex:{
type: Number,
default () {
return false
}
}
},
computed: {
md(){
return this.msg.content
},
nodes() {
let htmlString =
''
// 修改转换结果的htmlString值 用于正确给界面增加鼠标闪烁的效果
...
...
@@ -143,6 +182,103 @@
</
script
>
<
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 */
@import
"@/components/uni-ai-msg/uni-ai-msg.scss"
;
/* #endif */
...
...
package.json
浏览文件 @
e89415cc
{
"id"
:
"uni-ai-chat"
,
"name"
:
"uni-ai-chat"
,
"version"
:
"1.0.1
1
"
,
"version"
:
"1.0.1
2
"
,
"description"
:
"基于uni-ai的聊天示例项目,支持流式、支持前文总结,云端一体"
,
"main"
:
"main.js"
,
"scripts"
:
{
...
...
pages/chat/chat.vue
浏览文件 @
e89415cc
...
...
@@ -2,30 +2,12 @@
<view
class=
"page"
>
<view
class=
"container"
>
<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"
>
<view
v-for=
"(msg,index) in msgList"
class=
"msg-item"
:key=
"index"
>
<view
class=
"create_time-box"
>
<uni-dateformat
class=
"create_time"
:date=
"msg.create_time"
format=
"MM/dd hh:mm:ss"
></uni-dateformat>
</view>
<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"
>
<uni-ai-msg
ref=
"msg"
v-for=
"(msgIndex,index) in msgLength"
:key=
"index"
:msgIndex=
"index"
:show-cursor=
"index == msgLength - 1 && msgLength%2 === 0 && sseIndex"
></uni-ai-msg>
<view
class=
"tip-ai-ing"
v-if=
"msgLength && msgLength%2 !== 0"
>
<text>
uni-ai正在思考中...
</text>
<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>
...
...
@@ -47,13 +29,15 @@
</view>
<view
class=
"textarea-box"
>
<textarea
v-model=
"content"
:cursor-spacing=
"15"
class=
"textarea"
:auto-height=
"!isWidescreen"
@
keyup.shift=
"onKeyup('shift')"
@
keydown.shift=
"onKeydown('shift')"
@
keydown.enter=
"onKeydown('enter')"
:disabled=
"inputBoxDisabled"
:placeholder=
"placeholderText"
:maxlength=
"-1"
:focus=
"focus"
@
keyup.shift=
"onKeyup('shift')"
@
keydown.shift=
"onKeydown('shift')"
@
keydown.enter=
"onKeydown('enter')"
:disabled=
"inputBoxDisabled"
:placeholder=
"placeholderText"
:maxlength=
"-1"
:focus=
"focus"
placeholder-class=
"input-placeholder"
></textarea>
</view>
<view
class=
"send-btn-box"
>
<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>
...
...
@@ -64,6 +48,9 @@
<
script
>
// 引入配置文件
import
config
from
'
@/config.js
'
;
import
{
msgList
}
from
'
@/pages/chat/msgList.js
'
;
// 获取广告id
const
{
adpid
...
...
@@ -81,6 +68,8 @@
return
{
// 使聊天窗口滚动到指定元素id的值
scrollIntoView
:
""
,
// 消息长度(个数)
msgLength
:
0
,
// 消息列表数据
msgList
:
[],
// 输入框的消息内容
...
...
@@ -93,7 +82,7 @@
isWidescreen
:
false
,
// 广告位id
adpid
,
focus
:
false
focus
:
false
}
},
computed
:
{
...
...
@@ -104,7 +93,7 @@
return
true
}
// 如果消息列表长度为奇数,则禁用输入框
return
!!
(
this
.
msgL
ist
.
length
&&
this
.
msgList
.
l
ength
%
2
!==
0
)
return
!!
(
this
.
msgL
ength
&&
this
.
msgL
ength
%
2
!==
0
)
},
// 输入框占位符文本
placeholderText
()
{
...
...
@@ -127,15 +116,24 @@
// 监听msgList变化,将其存储到本地缓存中
watch
:
{
// #ifdef H5
inputBoxDisabled
(
val
)
{
this
.
$nextTick
(()
=>
{
inputBoxDisabled
(
val
)
{
this
.
$nextTick
(()
=>
{
this
.
focus
=
!
val
console
.
log
(
'
this.focus
'
,
this
.
focus
);
// console.log('this.focus', this.focus);
})
},
// #endif
msgList
:
{
handler
(
msgList
)
{
let
msgLength
=
msgList
.
length
if
(
msgLength
!=
this
.
msgLength
){
this
.
msgLength
=
msgLength
this
.
$nextTick
(()
=>
{
this
.
updateLastMsg
(
msgList
[
msgLength
-
1
])
})
}
// 将msgList存储到本地缓存中
uni
.
setStorage
({
"
key
"
:
"
uni-ai-msg
"
,
...
...
@@ -147,6 +145,7 @@
}
},
async
mounted
()
{
// 如果存在广告位id且用户token未过期
if
(
this
.
adpid
&&
uniCloud
.
getCurrentUserInfo
().
tokenExpired
>
Date
.
now
())
{
// 查询当前用户的积分
...
...
@@ -174,8 +173,13 @@
// })
// }
this
.
msgList
=
uni
.
getStorageSync
(
'
uni-ai-msg
'
)
||
[]
let
_msgList
=
uni
.
getStorageSync
(
'
uni-ai-msg
'
)
||
[];
if
(
_msgList
.
length
){
msgList
.
push
(...
_msgList
)
}
this
.
msgList
=
msgList
// 如果上一次对话中 最后一条消息ai未回复。则一启动就自动重发。
let
length
=
this
.
msgList
.
length
...
...
@@ -236,20 +240,20 @@
},
methods
:
{
// #ifdef H5 && VUE2
onKeydown
(
keyname
)
{
if
(
keyname
==
'
shift
'
){
onKeydown
(
keyname
)
{
if
(
keyname
==
'
shift
'
)
{
//按下了shift键
shiftKeyPressed
=
true
;
}
// 按下了回车 且 之前没按下 shift
if
(
keyname
==
'
enter
'
&&
!
shiftKeyPressed
)
{
this
.
$nextTick
(()
=>
{
this
.
$nextTick
(()
=>
{
this
.
beforeSendMsg
();
})
}
},
onKeyup
(
keyname
)
{
if
(
keyname
==
'
shift
'
){
onKeyup
(
keyname
)
{
if
(
keyname
==
'
shift
'
)
{
//按下了shift键
shiftKeyPressed
=
false
;
}
...
...
@@ -269,7 +273,7 @@
},
// 更新最后一条消息
updateLastMsg
(
param
)
{
let
length
=
this
.
msgL
ist
.
l
ength
let
length
=
this
.
msgLength
if
(
length
===
0
)
{
return
}
...
...
@@ -476,7 +480,7 @@
// console.log('sseChannel',sseChannel);
// 监听message事件
sseChannel
.
on
(
'
message
'
,
(
message
)
=>
{
sseChannel
.
on
(
'
message
'
,
(
message
)
=>
{
// console.log('on message', message);
// 将从云端接收到的消息添加到消息列表中
...
...
@@ -556,10 +560,17 @@
// console.log(res, res.reply);
// 判断是否要跳过本次回调,防止请求未返回时,历史对话已被清空。引起对话顺序错误 导致 对话输入框卡住
if
(
!
skip_callback
)
{
let
{
"
reply
"
:
content
,
summarize
,
insufficientScore
,
illegal
}
=
res
.
data
let
{
"
reply
"
:
content
,
summarize
,
insufficientScore
,
illegal
}
=
res
.
data
if
(
illegal
)
{
// 如果返回的数据包含illegal属性,就更新最后一条消息的illegal属性为true
this
.
updateLastMsg
({
illegal
:
true
})
this
.
updateLastMsg
({
illegal
:
true
})
}
// 将从云端接收到的消息添加到消息列表中
this
.
msgList
.
push
({
...
...
@@ -577,12 +588,16 @@
illegal
})
// 滚动窗口以显示最新的一条消息
this
.
$nextTick
(()
=>
{
this
.
$nextTick
(()
=>
{
this
.
showLastMsg
()
})
}
else
{
console
.
log
(
'
用户点击了清空按钮,跳过前一次请求的回调。内容:
'
,
res
.
data
.
reply
);
}
}
else
{
// 处理 sseChannel没结束 云函数提前结束的情况
sseChannel
.
close
()
this
.
sseIndex
=
0
}
})
.
catch
(
e
=>
{
...
...
@@ -593,6 +608,7 @@
// 如果最后一条消息的来源是人工智能机器人 就将流式响应计数值设置为0
if
(
l
&&
sseChannel
&&
this
.
msgList
[
l
-
1
].
isAi
)
{
sseChannel
.
close
()
this
.
sseIndex
=
0
}
...
...
@@ -659,7 +675,7 @@
// 将流式响应计数值归零
this
.
sseIndex
=
0
// 将消息列表清空
this
.
msgList
=
[]
this
.
msgList
.
splice
(
0
,
this
.
msgLength
);
}
}
});
...
...
@@ -748,6 +764,7 @@
.
textarea-box
.
textarea
:
:-
webkit-scrollbar
{
width
:
0
;
}
/* #endif */
.input-placeholder
{
...
...
@@ -783,7 +800,6 @@
.
send
:
:
after
{
display
:
none
;
}
/* #endif */
...
...
@@ -792,92 +808,6 @@
height
:
1px
;
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
{
margin-top
:
15px
;
text-align
:
center
;
...
...
@@ -886,7 +816,6 @@
font-size
:
12px
;
justify-content
:
center
;
}
.tip-ai-ing
{
align-items
:
center
;
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 @@
"uni-cloud-ai"
:
{}
},
"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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录