Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
J
jinchat-server
概览
概览
详情
活动
周期分析
版本库
存储库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
问题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
aigc-pioneer
jinchat-server
Commits
01bf39bc
提交
01bf39bc
authored
5月 13, 2023
作者:
fxjhello
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
文件接口
上级
a256f25e
隐藏空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
184 行增加
和
125 行删除
+184
-125
chat.ts
views/src/api/chat.ts
+5
-7
index.vue
views/src/views/chat/index.vue
+33
-51
index.vue
views/src/views/chat/layout/sider/index.vue
+30
-35
filelist.vue
...s/src/views/chat/layout/sider/knowledge-base/filelist.vue
+40
-31
index.vue
views/src/views/chat/layout/sider/knowledge-base/index.vue
+74
-0
vite.config.ts
views/vite.config.ts
+2
-1
没有找到文件。
views/src/api/chat.ts
浏览文件 @
01bf39bc
...
@@ -3,7 +3,7 @@ import { api } from './api'
...
@@ -3,7 +3,7 @@ import { api } from './api'
export
const
chat
=
(
params
:
any
)
=>
{
export
const
chat
=
(
params
:
any
)
=>
{
return
api
({
return
api
({
url
:
'/chat
-docs/chatno
'
,
url
:
'/chat'
,
method
:
'post'
,
method
:
'post'
,
data
:
JSON
.
stringify
(
params
),
data
:
JSON
.
stringify
(
params
),
})
})
...
@@ -17,20 +17,18 @@ export const chatfile = (params: any) => {
...
@@ -17,20 +17,18 @@ export const chatfile = (params: any) => {
})
})
}
}
export
const
getfilelist
=
()
=>
{
export
const
getfilelist
=
(
knowledge_base_id
:
any
)
=>
{
return
api
({
return
api
({
url
:
'/
chat-docs/list
'
,
url
:
'/
local_doc_qa/list_files
'
,
method
:
'get'
,
method
:
'get'
,
params
:
{
params
:
{
knowledge_base_id
},
knowledge_base_id
:
'123'
,
},
})
})
}
}
export
const
deletefile
=
(
params
:
any
)
=>
{
export
const
deletefile
=
(
params
:
any
)
=>
{
return
api
({
return
api
({
url
:
'/
chat-docs/delet
e'
,
url
:
'/
local_doc_qa/delete_fil
e'
,
method
:
'post'
,
method
:
'post'
,
data
:
JSON
.
stringify
(
params
),
data
:
JSON
.
stringify
(
params
),
})
})
...
...
views/src/views/chat/index.vue
浏览文件 @
01bf39bc
...
@@ -13,19 +13,18 @@ import HeaderComponent from './components/Header/index.vue'
...
@@ -13,19 +13,18 @@ import HeaderComponent from './components/Header/index.vue'
import
{
HoverButton
,
SvgIcon
}
from
'@/components/common'
import
{
HoverButton
,
SvgIcon
}
from
'@/components/common'
import
{
useBasicLayout
}
from
'@/hooks/useBasicLayout'
import
{
useBasicLayout
}
from
'@/hooks/useBasicLayout'
import
{
useChatStore
,
usePromptStore
}
from
'@/store'
import
{
useChatStore
,
usePromptStore
}
from
'@/store'
import
{
fetchChatAPIProcess
}
from
'@/api'
import
{
t
}
from
'@/locales'
import
{
t
}
from
'@/locales'
import
{
chat
,
chatfile
}
from
'@/api/chat'
import
{
chat
,
chatfile
}
from
'@/api/chat'
let
controller
=
new
AbortController
()
let
controller
=
new
AbortController
()
const
openLongReply
=
import
.
meta
.
env
.
VITE_GLOB_OPEN_LONG_REPLY
===
'true'
//
const openLongReply = import.meta.env.VITE_GLOB_OPEN_LONG_REPLY === 'true'
const
route
=
useRoute
()
const
route
=
useRoute
()
const
dialog
=
useDialog
()
const
dialog
=
useDialog
()
const
ms
=
useMessage
()
const
ms
=
useMessage
()
const
chatStore
=
useChatStore
()
const
chatStore
=
useChatStore
()
const
history
=
ref
<
any
>
([])
const
{
isMobile
}
=
useBasicLayout
()
const
{
isMobile
}
=
useBasicLayout
()
const
{
addChat
,
updateChat
,
updateChatSome
,
getChatByUuidAndIndex
}
=
useChat
()
const
{
addChat
,
updateChat
,
updateChatSome
,
getChatByUuidAndIndex
}
=
useChat
()
const
{
scrollRef
,
scrollToBottom
,
scrollToBottomIfAtBottom
}
=
useScroll
()
const
{
scrollRef
,
scrollToBottom
,
scrollToBottomIfAtBottom
}
=
useScroll
()
...
@@ -61,7 +60,9 @@ function handleSubmit() {
...
@@ -61,7 +60,9 @@ function handleSubmit() {
async
function
onConversation
()
{
async
function
onConversation
()
{
const
message
=
prompt
.
value
const
message
=
prompt
.
value
dataSources
.
value
.
forEach
((
item
)
=>
{
console
.
log
(
item
)
})
if
(
loading
.
value
)
if
(
loading
.
value
)
return
return
...
@@ -110,13 +111,13 @@ async function onConversation() {
...
@@ -110,13 +111,13 @@ async function onConversation() {
const
lastText
=
''
const
lastText
=
''
const
fetchChatAPIOnce
=
async
()
=>
{
const
fetchChatAPIOnce
=
async
()
=>
{
const
res
=
active
.
value
const
res
=
active
.
value
?
await
chatfile
({
message
})
?
await
chatfile
({
question
:
message
,
history
:
history
.
value
,
})
:
await
chat
({
:
await
chat
({
question
:
message
,
question
:
message
,
history
:
[[
history
:
history
.
value
,
'工伤保险是什么?'
,
'工伤保险是指用人单位按照国家规定,为本单位的职工和用人单位的其他人员,缴纳工伤保险费,由保险机构按照国家规定的标准,给予工伤保险待遇的社会保险制度。'
,
]],
})
})
const
result
=
active
.
value
?
res
.
data
.
response
.
text
:
res
.
data
.
response
const
result
=
active
.
value
?
res
.
data
.
response
.
text
:
res
.
data
.
response
updateChat
(
updateChat
(
...
@@ -230,7 +231,6 @@ async function onConversation() {
...
@@ -230,7 +231,6 @@ async function onConversation() {
loading
.
value
=
false
loading
.
value
=
false
}
}
}
}
async
function
onRegenerate
(
index
:
number
)
{
async
function
onRegenerate
(
index
:
number
)
{
if
(
loading
.
value
)
if
(
loading
.
value
)
return
return
...
@@ -239,7 +239,7 @@ async function onRegenerate(index: number) {
...
@@ -239,7 +239,7 @@ async function onRegenerate(index: number) {
const
{
requestOptions
}
=
dataSources
.
value
[
index
]
const
{
requestOptions
}
=
dataSources
.
value
[
index
]
le
t
message
=
requestOptions
?.
prompt
??
''
cons
t
message
=
requestOptions
?.
prompt
??
''
let
options
:
Chat
.
ConversationRequest
=
{}
let
options
:
Chat
.
ConversationRequest
=
{}
...
@@ -263,48 +263,30 @@ async function onRegenerate(index: number) {
...
@@ -263,48 +263,30 @@ async function onRegenerate(index: number) {
)
)
try
{
try
{
le
t
lastText
=
''
cons
t
lastText
=
''
const
fetchChatAPIOnce
=
async
()
=>
{
const
fetchChatAPIOnce
=
async
()
=>
{
await
fetchChatAPIProcess
<
Chat
.
ConversationResponse
>
({
const
res
=
active
.
value
prompt
:
message
,
?
await
chatfile
({
message
})
options
,
:
await
chat
({
signal
:
controller
.
signal
,
question
:
message
,
onDownloadProgress
:
({
event
})
=>
{
history
:
history
.
value
,
const
xhr
=
event
.
target
})
const
{
responseText
}
=
xhr
const
result
=
active
.
value
?
res
.
data
.
response
.
text
:
res
.
data
.
response
// Always process the final line
updateChat
(
const
lastIndex
=
responseText
.
lastIndexOf
(
'
\
n'
,
responseText
.
length
-
2
)
+
uuid
,
let
chunk
=
responseText
dataSources
.
value
.
length
-
1
,
if
(
lastIndex
!==
-
1
)
{
chunk
=
responseText
.
substring
(
lastIndex
)
dateTime
:
new
Date
().
toLocaleString
(),
try
{
text
:
lastText
+
(
result
??
''
),
const
data
=
JSON
.
parse
(
chunk
)
inversion
:
false
,
updateChat
(
error
:
false
,
+
uuid
,
loading
:
false
,
index
,
conversationOptions
:
null
,
{
requestOptions
:
{
prompt
:
message
,
options
:
{
...
options
}
},
dateTime
:
new
Date
().
toLocaleString
(),
text
:
lastText
+
(
data
.
text
??
''
),
inversion
:
false
,
error
:
false
,
loading
:
true
,
conversationOptions
:
{
conversationId
:
data
.
conversationId
,
parentMessageId
:
data
.
id
},
requestOptions
:
{
prompt
:
message
,
options
:
{
...
options
}
},
},
)
if
(
openLongReply
&&
data
.
detail
.
choices
[
0
].
finish_reason
===
'length'
)
{
options
.
parentMessageId
=
data
.
id
lastText
=
data
.
text
message
=
''
return
fetchChatAPIOnce
()
}
}
catch
(
error
)
{
//
}
},
},
})
)
scrollToBottomIfAtBottom
()
loading
.
value
=
false
updateChatSome
(
+
uuid
,
index
,
{
loading
:
false
})
updateChatSome
(
+
uuid
,
index
,
{
loading
:
false
})
}
}
await
fetchChatAPIOnce
()
await
fetchChatAPIOnce
()
...
...
views/src/views/chat/layout/sider/index.vue
浏览文件 @
01bf39bc
<
script
setup
lang=
'ts'
>
<
script
setup
lang=
'ts'
>
import
type
{
CSSProperties
}
from
'vue'
import
type
{
CSSProperties
}
from
'vue'
import
{
computed
,
ref
,
watch
}
from
'vue'
import
{
computed
,
ref
,
watch
}
from
'vue'
import
{
NButton
,
NLayoutSider
,
N
Upload
}
from
'naive-ui'
import
{
NButton
,
NLayoutSider
,
N
RadioButton
,
NRadioGroup
}
from
'naive-ui'
import
List
from
'./List.vue'
import
List
from
'./List.vue'
import
filelist
from
'./filelist
.vue'
import
Knowledge
from
'./knowledge-base/index
.vue'
import
Footer
from
'./Footer.vue'
import
Footer
from
'./Footer.vue'
import
{
useAppStore
,
useChatStore
}
from
'@/store'
import
{
useAppStore
,
useChatStore
}
from
'@/store'
import
{
useBasicLayout
}
from
'@/hooks/useBasicLayout'
import
{
useBasicLayout
}
from
'@/hooks/useBasicLayout'
...
@@ -46,6 +46,25 @@ const mobileSafeArea = computed(() => {
...
@@ -46,6 +46,25 @@ const mobileSafeArea = computed(() => {
}
}
return
{}
return
{}
})
})
const
songs
=
[
{
value
:
1
,
label
:
'会话'
,
},
{
value
:
2
,
label
:
'模型'
,
},
{
value
:
3
,
label
:
'知识库'
,
},
{
value
:
4
,
label
:
'提示词'
,
},
]
//
//
watch
(
watch
(
isMobile
,
isMobile
,
...
@@ -75,39 +94,20 @@ watch(
...
@@ -75,39 +94,20 @@ watch(
<div
class=
"flex flex-col h-full "
:style=
"mobileSafeArea"
>
<div
class=
"flex flex-col h-full "
:style=
"mobileSafeArea"
>
<main
class=
"flex flex-col flex-1 min-h-0"
>
<main
class=
"flex flex-col flex-1 min-h-0"
>
<div
class=
" flex justify-between"
>
<div
class=
" flex justify-between"
>
<NButton
dashed
@
click=
"menu = 1"
>
<NRadioGroup
v-model:value=
"menu"
name=
"radiobuttongroup1"
>
会话
<NRadioButton
</NButton>
v-for=
"song in songs"
<NButton
dashed
@
click=
"menu = 2"
>
:key=
"song.value"
模型
:value=
"song.value"
</NButton>
:label=
"song.label"
<NButton
dashed
@
click=
"menu = 3"
>
/>
知识库
</NRadioGroup>
</NButton>
<NButton
dashed
@
click=
"menu = 4"
>
提示词
</NButton>
</div>
</div>
<!-- 知识库界面 -->
<!-- 知识库界面 -->
<div
v-if=
"menu === 3"
>
<div
v-if=
"menu === 3"
>
<div
class=
"p-4"
>
<div
class=
"p-4"
>
<NUpload
<Knowledge
/>
action=
"http://127.0.0.1:1002/api/chat-docs/uploadone"
:headers=
"
{
'naive-info': 'hello!',
}"
:data="{
knowledge_base_id: '123',
}"
>
<NButton
block
>
文件上传
</NButton>
</NUpload>
</div>
<div
class=
"p-2 flex-1 min-h-0 pb-4 overflow-hidden"
>
<filelist
/>
</div>
</div>
</div>
</div>
<!-- 会话界面 -->
<!-- 会话界面 -->
...
@@ -121,11 +121,6 @@ watch(
...
@@ -121,11 +121,6 @@ watch(
<List
/>
<List
/>
</div>
</div>
</div>
</div>
<!--
<div
class=
"p-4"
>
<NButton
block
@
click=
"show = true"
>
{{
$t
(
'store.siderButton'
)
}}
</NButton>
</div>
-->
</main>
</main>
</div>
</div>
</NLayoutSider>
</NLayoutSider>
...
...
views/src/views/chat/layout/sider/filelist.vue
→
views/src/views/chat/layout/sider/
knowledge-base/
filelist.vue
浏览文件 @
01bf39bc
<
script
setup
lang=
'ts'
>
<
script
setup
lang=
'ts'
>
import
{
onMounted
,
ref
}
from
'vue'
import
{
onMounted
,
ref
,
toRef
}
from
'vue'
import
{
NInput
,
NP
opconfirm
,
NScrollba
r
}
from
'naive-ui'
import
{
NInput
,
NP
,
NPopconfirm
,
NScrollbar
,
NText
,
NUpload
,
NUploadDragge
r
}
from
'naive-ui'
import
{
SvgIcon
}
from
'@/components/common'
import
{
SvgIcon
}
from
'@/components/common'
import
{
useAppStore
,
useChatStore
}
from
'@/store'
import
{
useChatStore
}
from
'@/store'
import
{
useBasicLayout
}
from
'@/hooks/useBasicLayout'
import
{
deletefile
,
getfilelist
}
from
'@/api/chat'
import
{
deletefile
,
getfilelist
}
from
'@/api/chat'
const
knowledge
=
defineProps
({
knowledgebaseid
:
{
type
:
String
,
// 类型字符串
},
})
const
knowledge_base_id
=
toRef
(
knowledge
,
'knowledgebaseid'
)
const
{
isMobile
}
=
useBasicLayout
()
const
appStore
=
useAppStore
()
const
chatStore
=
useChatStore
()
const
chatStore
=
useChatStore
()
const
dataSources
=
ref
<
any
[]
>
([])
const
dataSources
=
ref
<
any
>
([])
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
const
res
=
await
getfilelist
()
const
res
=
await
getfilelist
(
knowledge_base_id
.
value
)
dataSources
.
value
=
res
.
data
.
data
dataSources
.
value
=
res
.
data
.
data
})
})
async
function
handleSelect
({
uuid
}:
Chat
.
History
)
{
if
(
isActive
(
uuid
))
return
if
(
chatStore
.
active
)
chatStore
.
updateHistory
(
chatStore
.
active
,
{
isEdit
:
false
})
await
chatStore
.
setActive
(
uuid
)
if
(
isMobile
.
value
)
appStore
.
setSiderCollapsed
(
true
)
}
/* function handleEdit({ uuid }: Chat.History, isEdit: boolean, event?: MouseEvent) {
/* function handleEdit({ uuid }: Chat.History, isEdit: boolean, event?: MouseEvent) {
event?.stopPropagation()
event?.stopPropagation()
chatStore.updateHistory(uuid, { isEdit })
chatStore.updateHistory(uuid, { isEdit })
} */
} */
async
function
handleDelete
(
item
:
any
)
{
async
function
handleDelete
(
item
:
any
)
{
/* const mid = */
await
deletefile
({
knowledge_base_id
:
'123'
,
doc_name
:
item
})
/* const mid = */
await
deletefile
({
knowledge_base_id
:
knowledge_base_id
.
value
,
doc_name
:
item
})
const
res
=
await
getfilelist
()
const
res
=
await
getfilelist
(
knowledge_base_id
.
value
)
dataSources
.
value
=
res
.
data
.
data
dataSources
.
value
=
res
.
data
.
data
}
}
...
@@ -45,13 +35,34 @@ function handleEnter({ uuid }: Chat.History, isEdit: boolean, event: KeyboardEve
...
@@ -45,13 +35,34 @@ function handleEnter({ uuid }: Chat.History, isEdit: boolean, event: KeyboardEve
if
(
event
.
key
===
'Enter'
)
if
(
event
.
key
===
'Enter'
)
chatStore
.
updateHistory
(
uuid
,
{
isEdit
})
chatStore
.
updateHistory
(
uuid
,
{
isEdit
})
}
}
function
isActive
(
uuid
:
number
)
{
return
chatStore
.
active
===
uuid
}
</
script
>
</
script
>
<
template
>
<
template
>
<NUpload
multiple
directory-dnd
action=
"http://192.168.1.128:1002/api/local_doc_qa/upload_file"
:headers=
"
{
'naive-info': 'hello!',
}"
:data="{
knowledge_base_id: knowledge.knowledgebaseid as string,
}"
>
<NUploadDragger>
<!--
<div
style=
"margin-bottom: 12px"
>
<NIcon
size=
"48"
:depth=
"3"
>
<archive-icon
/>
</NIcon>
</div>
-->
<NText
style=
"font-size: 16px"
>
点击或者拖动文件到该区域来上传
</NText>
<NP
depth=
"3"
style=
"margin: 8px 0 0 0"
>
在弹出的文件选择框,按住ctrl或shift进行多选
</NP>
</NUploadDragger>
</NUpload>
<NScrollbar
class=
"px-4"
>
<NScrollbar
class=
"px-4"
>
<div
class=
"flex flex-col gap-2 text-sm"
>
<div
class=
"flex flex-col gap-2 text-sm"
>
<template
v-if=
"!dataSources.length"
>
<template
v-if=
"!dataSources.length"
>
...
@@ -64,8 +75,6 @@ function isActive(uuid: number) {
...
@@ -64,8 +75,6 @@ function isActive(uuid: number) {
<div
v-for=
"(item, index) of dataSources"
:key=
"index"
>
<div
v-for=
"(item, index) of dataSources"
:key=
"index"
>
<a
<a
class=
"relative flex items-center gap-3 px-3 py-3 break-all border rounded-md cursor-pointer hover:bg-neutral-100 group dark:border-neutral-800 dark:hover:bg-[#24272e]"
class=
"relative flex items-center gap-3 px-3 py-3 break-all border rounded-md cursor-pointer hover:bg-neutral-100 group dark:border-neutral-800 dark:hover:bg-[#24272e]"
:class=
"isActive(item.uuid) && ['border-[#4b9e5f]', 'bg-neutral-100', 'text-[#4b9e5f]', 'dark:bg-[#24272e]', 'dark:border-[#4b9e5f]', 'pr-14']"
@
click=
"handleSelect(item)"
>
>
<span>
<span>
<SvgIcon
icon=
"ri:message-3-line"
/>
<SvgIcon
icon=
"ri:message-3-line"
/>
...
@@ -91,10 +100,10 @@ function isActive(uuid: number) {
...
@@ -91,10 +100,10 @@ function isActive(uuid: number) {
<NPopconfirm
placement=
"bottom"
@
positive-click=
"handleDelete(item)"
>
<NPopconfirm
placement=
"bottom"
@
positive-click=
"handleDelete(item)"
>
<template
#
trigger
>
<template
#
trigger
>
<button
class=
"p-1"
>
<button
class=
"p-1"
>
<
SvgIcon
icon=
"ri:delete-bin-line"
/
>
<
!--
<SvgIcon
icon=
"ri:delete-bin-line"
/>
--
>
</button>
</button>
</
template
>
</
template
>
{{ $t('chat.deleteHistoryConfirm') }}
确定删除此文件?
</NPopconfirm>
</NPopconfirm>
</template>
</template>
</div>
</div>
...
...
views/src/views/chat/layout/sider/knowledge-base/index.vue
0 → 100644
浏览文件 @
01bf39bc
<
script
setup
lang=
'ts'
>
import
{
NButton
,
NForm
,
NFormItem
,
NInput
}
from
'naive-ui'
import
{
onMounted
,
ref
}
from
'vue'
import
filelist
from
'./filelist.vue'
import
{
getfilelist
}
from
'@/api/chat'
const
items
=
ref
<
any
>
([])
onMounted
(
async
()
=>
{
const
res
=
await
getfilelist
({})
res
.
data
.
data
.
forEach
((
item
:
any
)
=>
{
items
.
value
.
push
({
value
:
item
,
show
:
false
,
})
})
})
const
formValue
=
ref
({
user
:
{
name
:
''
,
},
})
const
rules
=
{
user
:
{
name
:
{
required
:
true
,
message
:
'请输入名称'
,
trigger
:
'blur'
,
},
},
}
const
handleValidateClick
=
(
item
:
any
)
=>
{
items
.
value
.
forEach
((
res
:
{
value
:
any
;
show
:
boolean
})
=>
{
if
(
res
.
value
===
item
)
res
.
show
=
!
res
.
show
},
)
}
const
handleClick
=
()
=>
{
if
(
formValue
.
value
.
user
.
name
.
trim
()
!==
''
)
{
items
.
value
.
push
({
value
:
formValue
.
value
.
user
.
name
.
trim
(),
show
:
false
,
})
}
}
</
script
>
<
template
>
<NForm
ref=
"formRef"
inline
:label-width=
"80"
:model=
"formValue"
:rules=
"rules"
>
<NFormItem
label=
""
path=
"user.name"
>
<NInput
v-model:value=
"formValue.user.name"
placeholder=
"起个知识库名吧!"
/>
</NFormItem>
<NFormItem>
<NButton
attr-type=
"button"
@
click=
"handleClick"
>
新增
</NButton>
</NFormItem>
</NForm>
<div
v-for=
"item in items"
:key=
"item.value"
>
<NButton
block
size=
"large"
@
click=
"handleValidateClick(item.value)"
>
{{
item
.
value
}}
</NButton>
<div
v-if=
"item.show"
class=
"p-2 flex-1 min-h-0 pb-4 overflow-hidden"
>
<filelist
v-if=
"item.value"
:knowledgebaseid=
"item.value"
/>
</div>
<br>
</div>
</
template
>
views/vite.config.ts
浏览文件 @
01bf39bc
...
@@ -37,7 +37,8 @@ export default defineConfig((env) => {
...
@@ -37,7 +37,8 @@ export default defineConfig((env) => {
open
:
false
,
open
:
false
,
proxy
:
{
proxy
:
{
'/api'
:
{
'/api'
:
{
target
:
'http://127.0.0.1:7861'
,
target
:
'http://146.56.190.29'
,
// target: 'http://127.0.0.1:7861',
changeOrigin
:
true
,
// 允许跨域
changeOrigin
:
true
,
// 允许跨域
rewrite
:
path
=>
path
.
replace
(
'/api/'
,
''
),
rewrite
:
path
=>
path
.
replace
(
'/api/'
,
''
),
},
},
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论