Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DCloud
uni-ai-chat
提交
560a93e7
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看板
提交
560a93e7
编写于
6月 08, 2023
作者:
DCloud_JSON
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
1.0.23
上级
ad9168bf
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
212 addition
and
77 deletion
+212
-77
changelog.md
changelog.md
+4
-0
components/llm-config/llm-config.vue
components/llm-config/llm-config.vue
+51
-29
components/uni-ai-msg/uni-ai-msg.vue
components/uni-ai-msg/uni-ai-msg.vue
+111
-28
manifest.json
manifest.json
+3
-0
package.json
package.json
+1
-1
pages/chat/chat.vue
pages/chat/chat.vue
+42
-19
未找到文件。
changelog.md
浏览文件 @
560a93e7
## 1.0.23(2023-06-08)
-
新增 支持单独删除某一次对话(注意:一次对话包含提问和回答2条消息)
-
更新 客户端网络请求超时时间(
`manifest.json`
->
`networkTimeout`
->
`request`
)设置为
`600000`
毫秒
[
详情参考
](
https://uniapp.dcloud.net.cn/collocation/manifest.html#networktimeout
)
-
修复 部分情况,消息发送失败 仍然会显示“ai正在思考中”的问题
## 1.0.22(2023-06-07)
-
修复 解决Vue3版本的微信小程序端 textarea的blur事件,会触发“清空会话”事件的问题
## 1.0.21(2023-06-06)
...
...
components/llm-config/llm-config.vue
浏览文件 @
560a93e7
<
template
>
<
template
>
<uni-popup
ref=
"popup"
type=
"top"
>
<view
class=
"box"
>
<text
class=
"title"
>
请选择llm的model
</text>
<radio-group
@
change=
"radioChange"
class=
"radio-group"
>
<label
class=
"item"
v-for=
"(
model, index) in models"
:key=
"model
"
>
<radio
:value=
"
model"
:checked=
"currentModel === model
"
class=
"radio"
/>
<view
class=
"item-title"
>
{{
model
}}
</view>
<label
class=
"item"
v-for=
"(
item, index) in models"
:key=
"item.value
"
>
<radio
:value=
"
item.value"
:checked=
"currentModel === item.value
"
class=
"radio"
/>
<view
class=
"item-title"
>
{{
item
.
text
}}
</view>
</label>
</radio-group>
<view
class=
"btn-box"
>
...
...
@@ -13,34 +13,56 @@
<view
@
click=
"confirm"
class=
"btn confirm"
>
确认
</view>
</view>
</view>
</uni-popup>
</
template
>
</uni-popup>
</
template
>
<
script
>
let
confirmCallback
=
()
=>
{}
export
default
{
name
:
"
llm-config
"
,
data
()
{
return
{
let
confirmCallback
=
()
=>
{}
export
default
{
name
:
"
llm-config
"
,
data
()
{
return
{
models
:
[
"
gpt-4
"
,
"
gpt-4-0314
"
,
"
gpt-4-32k
"
,
"
gpt-4-32k-0314
"
,
"
gpt-3.5-turbo
"
,
"
gpt-3.5-turbo-0301
"
{
text
:
"
gpt-4
"
,
value
:
"
gpt-4
"
},
{
text
:
"
gpt-4-0314
"
,
value
:
"
gpt-4-0314
"
},
{
text
:
"
gpt-4-32k
"
,
value
:
"
gpt-4-32k
"
},
{
text
:
"
gpt-4-32k-0314
"
,
value
:
"
gpt-4-32k-0314
"
},
{
text
:
"
gpt-3.5-turbo
"
,
value
:
"
gpt-3.5-turbo
"
},
{
text
:
"
gpt-3.5-turbo-0301
"
,
value
:
"
gpt-3.5-turbo-0301
"
},
{
text
:
"
都不选
"
,
value
:
""
}
],
currentModel
:
''
};
};
},
mounted
()
{
this
.
currentModel
=
uni
.
getStorageSync
(
'
uni-ai-chat-llmModel
'
)
},
},
methods
:
{
open
(
callback
){
confirmCallback
=
callback
this
.
$refs
.
popup
.
open
(
'
center
'
)
},
},
radioChange
(
event
)
{
console
.
log
(
'
event
'
,
event
.
detail
.
value
)
this
.
currentModel
=
event
.
detail
.
value
...
...
@@ -52,11 +74,11 @@
// console.log(this.models[this.current]);
confirmCallback
(
this
.
currentModel
)
this
.
$refs
.
popup
.
close
()
},
}
}
</
script
>
},
}
}
</
script
>
<
style
>
/* #ifndef APP-NVUE */
.box
,
...
...
@@ -73,8 +95,8 @@
.box
,
.title
,
.btn-box
{
width
:
250px
;
}
.box
{
.box
{
background-color
:
#fff
;
display
:
flex
;
flex-direction
:
column
;
...
...
@@ -163,5 +185,5 @@
/* transform-origin: 0 0; */
transform
:
scaleX
(
.5
);
}
/* #endif */
/* #endif */
</
style
>
\ No newline at end of file
components/uni-ai-msg/uni-ai-msg.vue
浏览文件 @
560a93e7
<
template
>
<view
class=
"msg-item"
>
<view
class=
"msg-item"
v-if=
"!msg.isDelete"
>
<view
class=
"create_time-box"
>
<uni-dateformat
class=
"create_time"
:date=
"msg.create_time"
format=
"MM/dd hh:mm:ss"
></uni-dateformat>
</view>
...
...
@@ -22,12 +22,21 @@
</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
class=
"menu-box"
:class=
'
{"menu-box-ai":msg.isAi}'>
<text
v-if=
"isLastMsg && msg.isAi"
title=
"换一个答案"
@
click=
"changeAnswer"
class=
"pointer change-answer"
>
⟳
</text>
<view
@
click=
"showMoreMenu = !showMoreMenu"
class=
"more-icon-box"
>
<text
class=
"more-icon pointer"
>
...
</text>
</view>
<template
v-if=
"showMoreMenu"
>
<view
class=
"more-menu"
>
<view
@
click=
"copy"
title=
"复制"
class=
"copy-icon pointer"
>
<view
class=
"copy-icon-a"
></view>
<view
class=
"copy-icon-b"
></view>
</view>
<uni-icons
class=
"remove-msg pointer"
@
click=
"removeMsg"
type=
"trash"
size=
"20"
color=
"#bbb"
></uni-icons>
</view>
<view
class=
"more-menu-mask"
@
click=
"showMoreMenu = false"
></view>
</
template
>
</view>
</view>
<uni-icons
v-if=
"isLastMsg && !msg.isAi && msg.state != 100 && msgStateIcon(msg)"
...
...
@@ -125,10 +134,12 @@
// 悬浮的复制按钮的上边距
top
:
"
-100px
"
,
msg
:
{
content
:
""
content
:
""
,
isDelete
:
false
},
msgIndexList
:
0
,
adpid
adpid
,
showMoreMenu
:
false
};
},
mounted
()
{
...
...
@@ -166,7 +177,7 @@
// 修改转换结果的htmlString值 用于正确给界面增加鼠标闪烁的效果
// 判断markdown中代码块标识符的数量是否为偶数
if
(
this
.
msgContent
.
split
(
"
```
"
).
length
%
2
)
{
htmlString
=
markdownIt
.
render
(
this
.
msgContent
+
'
<span class="cursor">|</span>
'
)
;
htmlString
=
markdownIt
.
render
(
this
.
msgContent
)
+
'
<span class="cursor">|</span>
'
;
}
else
{
htmlString
=
markdownIt
.
render
(
this
.
msgContent
)
+
'
\n
<span class="cursor">|</span>
'
;
}
...
...
@@ -211,7 +222,7 @@
console
.
log
({
attrs
});
let
{
"
code-data-index
"
:
codeDataIndex
,
"
class
"
:
className
}
=
attrs
if
(
className
==
'
copy-btn
'
){
console
.
log
(
'
codeDataList[codeDataIndex]
'
,
codeDataList
[
codeDataIndex
]);
//
console.log('codeDataList[codeDataIndex]',codeDataList[codeDataIndex]);
uni
.
setClipboardData
({
data
:
codeDataList
[
codeDataIndex
],
showToast
:
false
,
...
...
@@ -241,7 +252,13 @@
icon
:
'
none
'
});
}
})
})
this
.
showMoreMenu
=
false
},
// 删除消息
removeMsg
(){
this
.
$emit
(
'
removeMsg
'
,
this
.
msgIndex
)
this
.
showMoreMenu
=
false
}
}
}
...
...
@@ -273,8 +290,8 @@
.msgStateIcon
{
position
:
relative
;
top
:
5px
;
right
:
5
px
;
top
:
-
5px
;
right
:
1
px
;
align-self
:
center
;
}
...
...
@@ -300,7 +317,7 @@
.content
{
position
:
relative
;
/* #ifndef APP-NVUE */
max-width
:
calc
(
85%
-
1
5px
);
max-width
:
calc
(
85%
-
4
5px
);
/* #endif */
background-color
:
#FFF
;
border-radius
:
5px
;
...
...
@@ -313,9 +330,9 @@
/* #endif */
}
.
controller
{
.
menu-box
{
position
:
absolute
;
right
:
-25
px
;
left
:
-18
px
;
bottom
:
0
;
width
:
20px
;
flex-direction
:
column
;
...
...
@@ -323,26 +340,81 @@
justify-content
:
flex-end
;
}
.retry-icon
{
.menu-box-ai
{
left
:
auto
;
right
:
-20px
;
}
.change-answer
{
font-size
:
26px
;
color
:
#d4d4d4
;
height
:
25px
;
line-height
:
15px
;
margin-bottom
:
5px
;
position
:
relative
;
left
:
2px
;
}
.retry-icon
:hover
{
.pointer
{
cursor
:
pointer
;
}
.pointer
:hover
{
color
:
#BBB
;
}
.retry-icon
,
.copy-icon
{
cursor
:
pointer
;
.more-icon-box
{
justify-content
:
center
;
overflow
:
hidden
;
}
.copy-icon
{
.more-icon
{
color
:
#d4d4d4
;
transform
:
rotate
(
90deg
);
position
:
relative
;
height
:
25px
;
left
:
4px
;
font-size
:
16px
;
z-index
:
999
;
}
.more-menu
{
position
:
absolute
;
bottom
:
-10px
;
left
:
-30px
;
flex-direction
:
column
;
justify-content
:
space-around
;
width
:
30px
;
padding
:
2px
5px
;
height
:
60px
;
z-index
:
999
;
background-color
:
#FFF
;
box-shadow
:
0
0
20px
#eee
;
border-radius
:
3px
;
}
.more-menu-mask
{
position
:
fixed
;
width
:
100vw
;
height
:
100vh
;
top
:
0
;
left
:
0
;
z-index
:
998
;
}
.menu-box-ai
.more-menu
{
left
:auto
;
right
:
-32px
;
}
.more-menu
.pointer
{
width
:
20px
;
}
.copy-icon
{
position
:
relative
;
height
:
25px
;
width
:
20px
;
}
.copy-icon-a
,
.copy-icon-b
{
position
:
absolute
;
...
...
@@ -350,18 +422,29 @@
width
:
10px
;
height
:
12px
;
background-color
:
#FFF
;
left
:
2
px
;
top
:
2
px
;
left
:
3
px
;
top
:
4
px
;
border-radius
:
3px
;
}
.copy-icon-b
{
top
:
8px
;
left
:
6px
;
}
.copy-icon
:hover
.copy-icon-a
,
.copy-icon
:hover
.copy-icon-b
,
{
border-color
:
#bbb
;
}
.copy-icon-b
{
top
:
5px
;
left
:
5px
;
}
.remove-msg
{
position
:
relative
;
opacity
:
0
.7
;
}
.remove-msg
:hover
{
opacity
:
1
;
}
/* #ifndef APP-NVUE */
.
content
:
:
v-deep
rich-text
{
...
...
manifest.json
浏览文件 @
560a93e7
...
...
@@ -5,6 +5,9 @@
"versionName"
:
"1.0.0"
,
"versionCode"
:
"100"
,
"transformPx"
:
false
,
"networkTimeout"
:
{
"request"
:
600000
},
"app-plus"
:
{
"usingComponents"
:
true
,
"nvueStyleCompiler"
:
"uni-app"
,
...
...
package.json
浏览文件 @
560a93e7
{
"id"
:
"uni-ai-chat"
,
"name"
:
"uni-ai-chat"
,
"version"
:
"1.0.2
2
"
,
"version"
:
"1.0.2
3
"
,
"description"
:
"基于uni-ai的聊天示例项目,支持流式、支持前文总结,云端一体"
,
"main"
:
"main.js"
,
"scripts"
:
{
...
...
pages/chat/chat.vue
浏览文件 @
560a93e7
...
...
@@ -7,8 +7,8 @@
<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"
@
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"
>
:show-cursor=
"index == msgLength - 1 && msgLength%2 === 0 && sseIndex"
:isLastMsg=
"index == msgLength - 1"
@
removeMsg=
"removeMsg"
></uni-ai-msg>
<view
class=
"tip-ai-ing"
v-if=
"msgLength && msgLength%2 !== 0
&& lastMsgState != -100
"
>
<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"
...
...
@@ -35,9 +35,9 @@
<uni-icons
class=
"menu-item"
@
click=
"clearAllMsg"
type=
"trash"
size=
"24"
color=
"#888"
></uni-icons>
<uni-icons
class=
"menu-item"
@
click=
"setLLMmodel"
color=
"#555"
size=
"20px"
type=
"settings"
></uni-icons>
</view>
<view
class=
"textarea-box"
@
click=
"focus = true"
>
<view
class=
"textarea-box"
>
<textarea
v-model=
"content"
:cursor-spacing=
"15"
class=
"textarea"
:auto-height=
"!isWidescreen"
:placeholder=
"placeholderText"
:maxlength=
"-1"
:focus=
"focus"
@
blur=
"focus = false"
:placeholder=
"placeholderText"
:maxlength=
"-1"
placeholder-class=
"input-placeholder"
></textarea>
</view>
<view
class=
"send-btn-box"
:title=
"(msgLength && msgLength%2 !== 0) ? 'ai正在回复中不能发送':''"
>
...
...
@@ -68,8 +68,10 @@
let
uniCoTaskList
=
[]
// 定义终止并清空 云对象的任务列表中所有 任务的方法
uniCoTaskList
.
clear
=
function
(){
// 执行数组内的所有任务
uniCoTaskList
.
forEach
(
task
=>
task
.
abort
())
uniCoTaskList
.
slice
(
0
,
0
)
// 清空数组
uniCoTaskList
.
slice
(
0
,
uniCoTaskList
.
length
)
}
// 获取广告id
...
...
@@ -99,8 +101,7 @@
// 当前屏幕是否为宽屏
isWidescreen
:
false
,
// 广告位id
adpid
,
focus
:
false
,
adpid
,
llmModel
:
false
}
},
...
...
@@ -125,6 +126,15 @@
// 获取当前环境
NODE_ENV
()
{
return
process
.
env
.
NODE_ENV
},
//最后一条消息的状态
lastMsgState
(){
let
mLength
=
this
.
msgList
.
length
if
(
mLength
){
return
this
.
msgList
[
mLength
-
1
].
state
}
else
{
return
false
}
}
},
// 监听msgList变化,将其存储到本地缓存中
...
...
@@ -137,7 +147,8 @@
this
.
$nextTick
(()
=>
{
this
.
updateLastMsg
(
msgList
[
msgLength
-
1
])
})
}
}
msgList
=
msgList
.
filter
(
i
=>
i
.
isDelete
!==
true
)
// 将msgList存储到本地缓存中
uni
.
setStorage
({
"
key
"
:
"
uni-ai-msg
"
,
...
...
@@ -373,6 +384,26 @@
illegal
:
false
})
this
.
send
()
},
removeMsg
(
index
){
// #ifdef VUE3
this
.
msgList
[
index
].
isDelete
=
true
if
(
this
.
msgList
[
index
].
isAi
&&
this
.
msgList
[
index
-
1
]){
this
.
msgList
[
index
-
1
].
isDelete
=
true
}
else
if
(
this
.
msgList
[
index
+
1
]){
this
.
msgList
[
index
+
1
].
isDelete
=
true
}
// #endif
// #ifdef VUE2
this
.
$set
(
msgList
[
index
],
"
isDelete
"
,
true
)
msgList
.
slice
(
index
,
1
,
msgList
[
index
])
if
(
msgList
[
index
].
isAi
&&
msgList
[
index
-
1
]){
this
.
$set
(
msgList
[
index
-
1
],
"
isDelete
"
,
true
)
}
else
if
(
msgList
[
index
+
1
]){
this
.
$set
(
msgList
[
index
+
1
],
"
isDelete
"
,
true
)
}
// #endif
},
async
beforeSendMsg
()
{
if
(
this
.
inputBoxDisabled
){
...
...
@@ -465,7 +496,7 @@
let
messages
=
[]
// 复制一份,消息列表数据
let
msgs
=
JSON
.
parse
(
JSON
.
stringify
(
this
.
msgList
)
)
let
msgs
=
msgList
.
filter
(
i
=>
i
.
isDelete
!==
true
)
// 带总结的消息 index
let
findIndex
=
[...
msgs
].
reverse
().
findIndex
(
item
=>
item
.
summarize
)
// console.log('findIndex', findIndex)
...
...
@@ -681,14 +712,6 @@
},
// 清空消息列表
clearAllMsg
(
e
)
{
// #ifdef MP-WEIXIN && VUE3
console
.
log
(
'
clearAllMsg
'
,
e
);
if
(
e
&&
e
.
type
==
"
blur
"
){
console
.
log
(
'
补丁:解决Vue3版本的微信小程序端 textarea的blur事件,会触发“清空会话”事件的问题
'
);
return
false
}
// #endif
// 弹出确认清空聊天记录的提示框
uni
.
showModal
({
title
:
"
确认要清空聊天记录?
"
,
...
...
@@ -795,13 +818,13 @@
}
.textarea-box
.textarea
{
max-height
:
1
0
0px
;
max-height
:
1
2
0px
;
font-size
:
14px
;
/* #ifndef APP-NVUE */
overflow
:
auto
;
/* #endif */
width
:
450rpx
;
font-size
:
14px
;
font-size
:
14px
;
}
/* #ifdef H5 */
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录