af-mobile-client-vue3 1.4.53 → 1.4.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/App.vue +14 -2
- package/src/components/common/MateChat/MateChat.vue +248 -0
- package/src/components/common/MateChat/apiService.ts +254 -0
- package/src/components/common/MateChat/assets/035-avatar-13.svg +1 -0
- package/src/components/common/MateChat/components/MateChatHeader.vue +253 -0
- package/src/components/common/MateChat/components/PromptList/PromptList.vue +189 -0
- package/src/components/common/MateChat/components/PromptList/index.ts +1 -0
- package/src/components/common/MateChat/useMateChat.ts +212 -0
- package/src/components/common/otherCharge/ChargePrintSelectorAndRemarks.vue +137 -137
- package/src/components/common/otherCharge/CodePayment.vue +357 -357
- package/src/components/common/otherCharge/FileUploader.vue +602 -602
- package/src/components/common/otherCharge/GridFileUploader.vue +846 -846
- package/src/components/common/otherCharge/PaymentMethodSelector.vue +202 -202
- package/src/components/common/otherCharge/PaymentMethodSelectorCard.vue +45 -45
- package/src/components/common/otherCharge/ReceiptModal.vue +273 -273
- package/src/components/common/otherCharge/index.ts +43 -43
- package/src/components/data/OtherCharge/OtherChargeItemModal.vue +547 -547
- package/src/components/data/XFormGroup/doc/DeviceForm.vue +1 -1
- package/src/components/data/XFormGroup/doc/UserForm.vue +1 -1
- package/src/components/data/XReportGrid/XReportDemo.vue +33 -33
- package/src/components/data/XReportGrid/print.js +184 -184
- package/src/utils/queryFormDefaultRangePicker.ts +57 -57
- package/src/utils/timeUtil.ts +27 -27
- package/src/views/component/MateChat/MateChatView.vue +30 -233
- package/src/views/component/XCellListView/index.vue +107 -138
- package/src/views/component/XFormGroupView/index.vue +78 -82
- package/src/views/component/XFormView/index.vue +41 -46
- package/src/views/component/MateChat/apiService.ts +0 -104
package/package.json
CHANGED
package/src/App.vue
CHANGED
|
@@ -7,9 +7,21 @@ import { useHead } from '@unhead/vue'
|
|
|
7
7
|
import {
|
|
8
8
|
ConfigProvider as VanConfigProvider,
|
|
9
9
|
} from 'vant/es'
|
|
10
|
-
import { computed } from 'vue'
|
|
10
|
+
import { computed, getCurrentInstance } from 'vue'
|
|
11
|
+
import { useRoute } from 'vue-router'
|
|
11
12
|
import { appDescription, appName } from './constants'
|
|
12
13
|
|
|
14
|
+
const instance = getCurrentInstance()
|
|
15
|
+
const route = useRoute()
|
|
16
|
+
|
|
17
|
+
// 是否使用 fullPath 作为 component key
|
|
18
|
+
// 配置:是否使用 fullPath 作为 router-view 的 component key
|
|
19
|
+
// 该配置作用为:区分keep-alive缓存组件,为true时 如果跳转相同的缓存组件但是url携带参数不同 就会进行区分。反之则会只会根据路由名称进行命中
|
|
20
|
+
// 普通走基座的项目无需进行设置,只有特殊的单项目部署的服务 需要固定缓存顶层组件时进行开启,例如微信-开启方式在自己项目的main.ts中增加 app.config.globalProperties.$useFullPathKey = false
|
|
21
|
+
const useFullPathKey = computed(() => {
|
|
22
|
+
return instance?.appContext.config.globalProperties.$useFullPathKey ?? true
|
|
23
|
+
})
|
|
24
|
+
|
|
13
25
|
useHead({
|
|
14
26
|
title: appName,
|
|
15
27
|
meta: [
|
|
@@ -48,7 +60,7 @@ const mode = computed(() => {
|
|
|
48
60
|
<router-view v-slot="{ Component }">
|
|
49
61
|
<section class="app-wrapper">
|
|
50
62
|
<keep-alive :include="keepAliveRouteNames" :max="5">
|
|
51
|
-
<component :is="Component" :key="
|
|
63
|
+
<component :is="Component" :key="useFullPathKey ? route.fullPath : undefined" />
|
|
52
64
|
</keep-alive>
|
|
53
65
|
</section>
|
|
54
66
|
</router-view>
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import liuliLogo from '@af-mobile-client-vue3/assets/img/component/liuli.png'
|
|
3
|
+
import userAvatarImg from '@af-mobile-client-vue3/components/common/MateChat/assets/035-avatar-13.svg'
|
|
4
|
+
import MateChatHeader from '@af-mobile-client-vue3/components/common/MateChat/components/MateChatHeader.vue'
|
|
5
|
+
import { PromptList } from '@af-mobile-client-vue3/components/common/MateChat/components/PromptList'
|
|
6
|
+
// MateChat ai 对话相关
|
|
7
|
+
import { Button as VanButton } from 'vant'
|
|
8
|
+
import { defineProps, toRefs, withDefaults } from 'vue'
|
|
9
|
+
import { useMateChat } from './useMateChat'
|
|
10
|
+
import 'vant/es/image-preview/style'
|
|
11
|
+
|
|
12
|
+
interface MateChatPromptItem {
|
|
13
|
+
value: string
|
|
14
|
+
label: string
|
|
15
|
+
iconConfig?: {
|
|
16
|
+
name: string
|
|
17
|
+
color?: string
|
|
18
|
+
}
|
|
19
|
+
desc?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface MateChatProps {
|
|
23
|
+
/**
|
|
24
|
+
* 介绍文案,不传或为空数组时不展示 McIntroduction
|
|
25
|
+
*/
|
|
26
|
+
description?: string[]
|
|
27
|
+
/**
|
|
28
|
+
* 首屏推荐问题列表
|
|
29
|
+
*/
|
|
30
|
+
introPrompt?: MateChatPromptItem[]
|
|
31
|
+
/**
|
|
32
|
+
* 底部快捷问题列表
|
|
33
|
+
*/
|
|
34
|
+
simplePrompt?: MateChatPromptItem[]
|
|
35
|
+
/**
|
|
36
|
+
* 客服名称,默认:智能能客服
|
|
37
|
+
*/
|
|
38
|
+
serviceName?: string
|
|
39
|
+
/**
|
|
40
|
+
* 是否使用流式对话
|
|
41
|
+
*/
|
|
42
|
+
useStream?: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const props = withDefaults(defineProps<MateChatProps>(), {
|
|
46
|
+
description: () => [],
|
|
47
|
+
introPrompt: () => [],
|
|
48
|
+
simplePrompt: () => [],
|
|
49
|
+
serviceName: '智能客服',
|
|
50
|
+
useStream: false,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const { description, introPrompt, simplePrompt, serviceName, useStream } = toRefs(props)
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
startPage,
|
|
57
|
+
inputValue,
|
|
58
|
+
messages,
|
|
59
|
+
newConversation,
|
|
60
|
+
onSubmit,
|
|
61
|
+
} = useMateChat({
|
|
62
|
+
useStream: useStream.value,
|
|
63
|
+
})
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<div id="mate-chat-view">
|
|
68
|
+
<McLayout class="chat-card">
|
|
69
|
+
<MateChatHeader
|
|
70
|
+
v-if="!startPage"
|
|
71
|
+
:logo-image="liuliLogo"
|
|
72
|
+
:title="serviceName"
|
|
73
|
+
/>
|
|
74
|
+
<McLayoutContent
|
|
75
|
+
v-if="startPage"
|
|
76
|
+
style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px"
|
|
77
|
+
>
|
|
78
|
+
<McIntroduction
|
|
79
|
+
v-if="description && description.length"
|
|
80
|
+
:logo-img="liuliLogo"
|
|
81
|
+
:title="serviceName"
|
|
82
|
+
:sub-title="`Hi,我是${serviceName}`"
|
|
83
|
+
:description="description"
|
|
84
|
+
class="McIntroduction"
|
|
85
|
+
/>
|
|
86
|
+
<PromptList
|
|
87
|
+
v-if="introPrompt && introPrompt.length"
|
|
88
|
+
:list="introPrompt"
|
|
89
|
+
@item-click="onSubmit($event.label)"
|
|
90
|
+
/>
|
|
91
|
+
</McLayoutContent>
|
|
92
|
+
<McLayoutContent v-else class="content-container">
|
|
93
|
+
<template v-for="(msg, idx) in messages" :key="idx">
|
|
94
|
+
<McBubble
|
|
95
|
+
v-if="msg.from === 'user'"
|
|
96
|
+
:content="msg.content"
|
|
97
|
+
align="right"
|
|
98
|
+
:avatar-config="{ imgSrc: userAvatarImg }"
|
|
99
|
+
/>
|
|
100
|
+
<McBubble
|
|
101
|
+
v-else-if="msg.from === 'service'"
|
|
102
|
+
:content="msg.content"
|
|
103
|
+
:avatar-config="{ imgSrc: userAvatarImg }"
|
|
104
|
+
/>
|
|
105
|
+
<McBubble v-else :content="msg.content" :avatar-config="{ imgSrc: liuliLogo }" :loading="msg.loading">
|
|
106
|
+
<McMarkdownCard
|
|
107
|
+
:content="msg.content"
|
|
108
|
+
:typing="!useStream"
|
|
109
|
+
/>
|
|
110
|
+
</McBubble>
|
|
111
|
+
</template>
|
|
112
|
+
</McLayoutContent>
|
|
113
|
+
<div class="shortcut" style="display: flex; align-items: center; gap: 8px">
|
|
114
|
+
<PromptList
|
|
115
|
+
v-if="!startPage && simplePrompt && simplePrompt.length"
|
|
116
|
+
:list="simplePrompt"
|
|
117
|
+
direction="horizontal"
|
|
118
|
+
class="shortcut-prompt"
|
|
119
|
+
style="flex: 1"
|
|
120
|
+
@item-click="onSubmit($event.label)"
|
|
121
|
+
/>
|
|
122
|
+
<VanButton
|
|
123
|
+
style="margin-left: auto"
|
|
124
|
+
icon="add"
|
|
125
|
+
title="新建对话"
|
|
126
|
+
size="small"
|
|
127
|
+
round
|
|
128
|
+
@click="newConversation"
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
<McLayoutSender>
|
|
132
|
+
<McInput
|
|
133
|
+
:value="inputValue"
|
|
134
|
+
placeholder="请输入您的问题,我会为您解答"
|
|
135
|
+
:max-length="2000"
|
|
136
|
+
@change="(e) => (inputValue = e)"
|
|
137
|
+
@submit="onSubmit"
|
|
138
|
+
/>
|
|
139
|
+
</McLayoutSender>
|
|
140
|
+
</McLayout>
|
|
141
|
+
</div>
|
|
142
|
+
</template>
|
|
143
|
+
|
|
144
|
+
<style scoped lang="less">
|
|
145
|
+
#mate-chat-view {
|
|
146
|
+
/* 外层渐变背景容器 */
|
|
147
|
+
width: 100%;
|
|
148
|
+
min-height: 100vh;
|
|
149
|
+
background: linear-gradient(to bottom, #a8f0ed 0%, #a8e6cf 8%, #c5b3ff 12%, #e0b3ff 40%, #fff4b8 60%, #ffb8d1 90%);
|
|
150
|
+
/* background: linear-gradient(to bottom, #d0c9ff 0%, #e6d6f0 8%, #f1dbea 12%, #c8dcfb 40%, #abc6f6 60%, #87aefe 90%); */
|
|
151
|
+
padding: 20px;
|
|
152
|
+
display: flex;
|
|
153
|
+
justify-content: center;
|
|
154
|
+
align-items: flex-start;
|
|
155
|
+
box-sizing: border-box;
|
|
156
|
+
|
|
157
|
+
/* 中间白色圆角卡片 */
|
|
158
|
+
.chat-card {
|
|
159
|
+
width: 100%;
|
|
160
|
+
max-width: 1200px;
|
|
161
|
+
height: calc(100vh - 40px);
|
|
162
|
+
background: #ffffff;
|
|
163
|
+
border-radius: 24px;
|
|
164
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
|
|
165
|
+
overflow: hidden;
|
|
166
|
+
display: flex;
|
|
167
|
+
flex-direction: column;
|
|
168
|
+
transition: all 0.3s ease;
|
|
169
|
+
padding: 20px;
|
|
170
|
+
gap: 8px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.chat-card:hover {
|
|
174
|
+
box-shadow: 0 25px 70px rgba(0, 0, 0, 0.2);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* 移动端适配 */
|
|
178
|
+
@media (max-width: 768px) {
|
|
179
|
+
padding: 8px;
|
|
180
|
+
.chat-card {
|
|
181
|
+
height: calc(100vh - 16px);
|
|
182
|
+
border-radius: 16px;
|
|
183
|
+
padding: 8px;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.content-container {
|
|
188
|
+
display: flex;
|
|
189
|
+
flex-direction: column;
|
|
190
|
+
gap: 8px;
|
|
191
|
+
overflow: auto;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.input-foot-wrapper {
|
|
195
|
+
display: flex;
|
|
196
|
+
justify-content: space-between;
|
|
197
|
+
align-items: center;
|
|
198
|
+
width: 100%;
|
|
199
|
+
height: 100%;
|
|
200
|
+
margin-right: 8px;
|
|
201
|
+
|
|
202
|
+
.input-foot-left {
|
|
203
|
+
display: flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
gap: 8px;
|
|
206
|
+
|
|
207
|
+
span {
|
|
208
|
+
font-size: 14px;
|
|
209
|
+
line-height: 18px;
|
|
210
|
+
color: #252b3a;
|
|
211
|
+
cursor: pointer;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.input-foot-dividing-line {
|
|
215
|
+
width: 1px;
|
|
216
|
+
height: 14px;
|
|
217
|
+
background-color: #d7d8da;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.input-foot-maxlength {
|
|
221
|
+
font-size: 14px;
|
|
222
|
+
color: #71757f;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.input-foot-right {
|
|
227
|
+
.demo-button-content {
|
|
228
|
+
font-size: 14px;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
& > *:not(:first-child) {
|
|
232
|
+
margin-left: 8px;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
:deep(.mc-header-logo-container img) {
|
|
237
|
+
width: 32px;
|
|
238
|
+
height: 32px;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.McIntroduction {
|
|
242
|
+
:deep(.mc-introduction-logo-container img) {
|
|
243
|
+
width: 64px;
|
|
244
|
+
height: 64px;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
</style>
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios'
|
|
2
|
+
import axios from 'axios'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 聊天消息接口
|
|
6
|
+
*/
|
|
7
|
+
export interface ChatMessage {
|
|
8
|
+
role: 'user' | 'assistant' | 'system'
|
|
9
|
+
content: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 聊天请求参数接口
|
|
14
|
+
*/
|
|
15
|
+
export interface ChatCompletionsRequest {
|
|
16
|
+
chatId: string
|
|
17
|
+
stream: boolean
|
|
18
|
+
detail: boolean
|
|
19
|
+
variables: {
|
|
20
|
+
uid: string
|
|
21
|
+
name: string
|
|
22
|
+
}
|
|
23
|
+
messages: ChatMessage[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 聊天响应使用情况接口
|
|
28
|
+
*/
|
|
29
|
+
export interface ChatUsage {
|
|
30
|
+
prompt_tokens: number
|
|
31
|
+
completion_tokens: number
|
|
32
|
+
total_tokens: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 聊天响应选择项接口
|
|
37
|
+
*/
|
|
38
|
+
export interface ChatChoice {
|
|
39
|
+
message: {
|
|
40
|
+
role: 'assistant'
|
|
41
|
+
content: string
|
|
42
|
+
}
|
|
43
|
+
finish_reason: string
|
|
44
|
+
index: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 聊天响应接口
|
|
49
|
+
*/
|
|
50
|
+
export interface ChatCompletionsResponse {
|
|
51
|
+
id: string
|
|
52
|
+
model: string
|
|
53
|
+
usage: ChatUsage
|
|
54
|
+
choices: ChatChoice[]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 业务层聊天结果
|
|
59
|
+
*/
|
|
60
|
+
export interface ChatBizResult {
|
|
61
|
+
/**
|
|
62
|
+
* normal: 普通回复
|
|
63
|
+
* transfer: 转人工
|
|
64
|
+
*/
|
|
65
|
+
type: 'normal' | 'transfer'
|
|
66
|
+
/**
|
|
67
|
+
* 大模型原始返回内容
|
|
68
|
+
*/
|
|
69
|
+
content: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 流式对话回调
|
|
74
|
+
*/
|
|
75
|
+
export interface ChatStreamCallbacks {
|
|
76
|
+
/**
|
|
77
|
+
* 每次收到 FastGPT SSE 的增量内容时触发
|
|
78
|
+
*/
|
|
79
|
+
onMessage?: (chunk: string) => void
|
|
80
|
+
/**
|
|
81
|
+
* 流结束时触发(包括收到 [DONE] 或正常读取结束)
|
|
82
|
+
*/
|
|
83
|
+
onComplete?: () => void
|
|
84
|
+
/**
|
|
85
|
+
* 请求或解析发生异常时触发
|
|
86
|
+
*/
|
|
87
|
+
onError?: (error: unknown) => void
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 创建独立的 axios 实例,不经过拦截器
|
|
92
|
+
*/
|
|
93
|
+
const chatAxiosInstance: AxiosInstance = axios.create({
|
|
94
|
+
baseURL: import.meta.env.VITE_APP_API_BASE_URL || '',
|
|
95
|
+
timeout: 20000,
|
|
96
|
+
headers: {
|
|
97
|
+
'Content-Type': 'application/json',
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 发送聊天请求
|
|
103
|
+
* @param content 用户输入的内容
|
|
104
|
+
* @returns Promise<ChatCompletionsResponse>
|
|
105
|
+
*/
|
|
106
|
+
export function chatCompletions(content: string): Promise<ChatCompletionsResponse> {
|
|
107
|
+
const requestData: ChatCompletionsRequest = {
|
|
108
|
+
chatId: 'chatId13333113',
|
|
109
|
+
stream: false,
|
|
110
|
+
detail: false,
|
|
111
|
+
variables: {
|
|
112
|
+
uid: 'asdfadsfasfd2323',
|
|
113
|
+
name: '张三',
|
|
114
|
+
},
|
|
115
|
+
messages: [
|
|
116
|
+
{
|
|
117
|
+
role: 'user',
|
|
118
|
+
content,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return chatAxiosInstance.post<ChatCompletionsResponse>(
|
|
124
|
+
'/v1/chat/completions',
|
|
125
|
+
requestData,
|
|
126
|
+
{
|
|
127
|
+
headers: {
|
|
128
|
+
Authorization: 'Bearer fastgpt-f5FO1pkucnrEJvQsP4g8N9V3mUZNI396eiuef5PJjYty4vJztufB1',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
).then(response => response.data)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 封装后的业务聊天请求
|
|
136
|
+
* - 负责从大模型返回中解析出业务含义(普通回复 / 转人工)
|
|
137
|
+
* - UI 层只需要根据 type 决定渲染哪种气泡
|
|
138
|
+
*/
|
|
139
|
+
export async function chatBiz(content: string): Promise<ChatBizResult> {
|
|
140
|
+
const response = await chatCompletions(content)
|
|
141
|
+
|
|
142
|
+
if (!response.choices || response.choices.length === 0) {
|
|
143
|
+
throw new Error('响应数据格式错误')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const text = response.choices[0].message.content
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const parsed = JSON.parse(text) as { msgType?: string }
|
|
150
|
+
if (parsed.msgType === 'transfer') {
|
|
151
|
+
return {
|
|
152
|
+
type: 'transfer',
|
|
153
|
+
content: text,
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// 忽略解析错误,按普通文本处理
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
type: 'normal',
|
|
163
|
+
content: text,
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 使用 FastGPT /v1/chat/completions 的 stream=true 进行流式对话
|
|
169
|
+
* 参考官方文档: https://doc.fastgpt.io/docs/introduction/development/openapi/chat
|
|
170
|
+
*/
|
|
171
|
+
export async function chatCompletionsStream(
|
|
172
|
+
content: string,
|
|
173
|
+
callbacks: ChatStreamCallbacks,
|
|
174
|
+
): Promise<void> {
|
|
175
|
+
const { onMessage, onComplete, onError } = callbacks
|
|
176
|
+
|
|
177
|
+
const requestData: ChatCompletionsRequest = {
|
|
178
|
+
chatId: 'chatId13333113',
|
|
179
|
+
stream: true,
|
|
180
|
+
detail: false,
|
|
181
|
+
variables: {
|
|
182
|
+
uid: 'asdfadsfasfd2323',
|
|
183
|
+
name: '张三',
|
|
184
|
+
},
|
|
185
|
+
messages: [
|
|
186
|
+
{
|
|
187
|
+
role: 'user',
|
|
188
|
+
content,
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const baseUrl = import.meta.env.VITE_APP_API_BASE_URL || ''
|
|
195
|
+
const res = await fetch(`${baseUrl}/v1/chat/completions`, {
|
|
196
|
+
method: 'POST',
|
|
197
|
+
headers: {
|
|
198
|
+
'Content-Type': 'application/json',
|
|
199
|
+
'Authorization': 'Bearer fastgpt-f5FO1pkucnrEJvQsP4g8N9V3mUZNI396eiuef5PJjYty4vJztufB1',
|
|
200
|
+
},
|
|
201
|
+
body: JSON.stringify(requestData),
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
if (!res.ok || !res.body) {
|
|
205
|
+
throw new Error(`请求失败: ${res.status}`)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const reader = res.body.getReader()
|
|
209
|
+
const decoder = new TextDecoder('utf-8')
|
|
210
|
+
let buffer = ''
|
|
211
|
+
|
|
212
|
+
while (true) {
|
|
213
|
+
const { done, value } = await reader.read()
|
|
214
|
+
if (done)
|
|
215
|
+
break
|
|
216
|
+
|
|
217
|
+
buffer += decoder.decode(value, { stream: true })
|
|
218
|
+
const lines = buffer.split('\n')
|
|
219
|
+
buffer = lines.pop() ?? ''
|
|
220
|
+
|
|
221
|
+
for (const rawLine of lines) {
|
|
222
|
+
const line = rawLine.trim()
|
|
223
|
+
if (!line.startsWith('data:'))
|
|
224
|
+
continue
|
|
225
|
+
|
|
226
|
+
const dataStr = line.slice(5).trim()
|
|
227
|
+
if (!dataStr || dataStr === '[DONE]') {
|
|
228
|
+
onComplete?.()
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
const json = JSON.parse(dataStr) as {
|
|
234
|
+
choices?: Array<{ delta?: { content?: string } }>
|
|
235
|
+
}
|
|
236
|
+
const delta = json.choices?.[0]?.delta
|
|
237
|
+
const chunkText = delta?.content || ''
|
|
238
|
+
if (chunkText) {
|
|
239
|
+
onMessage?.(chunkText)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch (e) {
|
|
243
|
+
console.error('解析数据失败', e, dataStr)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
onComplete?.()
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
console.error('FastGPT 流式请求异常', error)
|
|
252
|
+
onError?.(error)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1764664883478" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17459" xmlns:xlink="http://www.w3.org/1999/xlink" width="192" height="192"><path d="M896 803.94V992H128v-188.06a128 128 0 0 1 96.96-124.18L384 640a57.878 57.878 0 0 0 32-51.78V480h192v108.22A57.878 57.878 0 0 0 640 640l159.04 39.76A128 128 0 0 1 896 803.94z" fill="#FDC8A2" p-id="17460"></path><path d="M608 480v101.34l-12.8 17.06c-1.02 1.36-2.08 2.66-3.2 3.92A63.984 63.984 0 0 1 544 624h-64a63.984 63.984 0 0 1-48-21.68c-1.12-1.26-2.18-2.56-3.2-3.92l-12.8-17.06V480z" fill="#FDBF92" p-id="17461"></path><path d="M873.72 731.78a128.2 128.2 0 0 0-74.68-52.02L640 640c-23.12 57.76-75.56 86.66-128 86.66s-104.88-28.9-128-86.66l-159.04 39.76A128 128 0 0 0 128 803.94V992h768v-188.06a127.624 127.624 0 0 0-22.28-72.16z" fill="#E9D6BB" p-id="17462"></path><path d="M672 240v186.66a64.034 64.034 0 0 1-12.8 38.4l-64 85.34c-1.02 1.36-2.08 2.66-3.2 3.92A63.984 63.984 0 0 1 544 576h-64a63.984 63.984 0 0 1-48-21.68c-1.12-1.26-2.18-2.56-3.2-3.92l-64-85.34a64.034 64.034 0 0 1-12.8-38.4V240c0-88.36 71.64-144 160-144 44.18 0 84.18 13.92 113.14 38.86S672 195.82 672 240z" fill="#FDC8A2" p-id="17463"></path><path d="M428 336a28 28 0 1 1 28-28 28.03 28.03 0 0 1-28 28zM596 336a28 28 0 1 1 28-28 28.03 28.03 0 0 1-28 28z" fill="#91563A" p-id="17464"></path><path d="M344 368h8v-112h-8a56 56 0 0 0-56 56 56 56 0 0 0 56 56zM680 256h-8v112h8a56 56 0 0 0 56-56 56 56 0 0 0-56-56z" fill="#FDB683" p-id="17465"></path><path d="M608 128l64 128V144a64 64 0 0 0-64-64zM352 256V128h64z" fill="#2B4266" p-id="17466"></path><path d="M608 80v48H352a79.75 79.75 0 0 1 23.44-56.56 81.26 81.26 0 0 1 12.08-9.94 79.324 79.324 0 0 1 29.62-12.12A80.848 80.848 0 0 1 432 48h124.22A57.878 57.878 0 0 1 608 80z" fill="#34507B" p-id="17467"></path><path d="M880 864h32v128h-32zM802.92 664.238l-157.2-39.3A41.854 41.854 0 0 1 624 588.22v-49.554l48-64.012a80.568 80.568 0 0 0 16-48v-43.11a71.988 71.988 0 0 0 0-143.088V144a80.102 80.102 0 0 0-70.528-79.42A73.4 73.4 0 0 0 556.222 32H432c-27.334 0-51.164 14.36-51.164 14.36l6.34 12.662L408.368 112H370a63.432 63.432 0 0 1 10.8-22.4l-25.6-19.22A95.2 95.2 0 0 0 336 128v112.456a71.988 71.988 0 0 0 0 143.088v43.116a80.578 80.578 0 0 0 16 48l48 64v49.56a41.634 41.634 0 0 1-21.718 36.718l-157.2 39.298A143.8 143.8 0 0 0 112 803.94V992h32v-188.06A111.8 111.8 0 0 1 161.366 744l51.32 51.318 22.628-22.628-53.064-53.07a111.828 111.828 0 0 1 46.6-24.338l146.1-36.526c26.208 52.094 77.538 83.904 137.05 83.904s110.842-31.8 137.058-83.904l146.102 36.528a111.8 111.8 0 0 1 46.6 24.336l-53.064 53.066 22.628 22.628L862.634 744A111.8 111.8 0 0 1 880 803.94V830h32v-26.06a143.786 143.786 0 0 0-109.08-139.702zM720 312a40.076 40.076 0 0 1-32 39.2v-78.4a40.076 40.076 0 0 1 32 39.2z m-64-168v44.222l-32-64v-25.48A48.084 48.084 0 0 1 656 144z m-64-59.904V112h-21.168l-19.2-48S577.5 63 592 84.096zM506.832 112l-19.2-48h29.536l19.2 48z m-53.664-48l19.2 48h-29.536l-18.988-47.47c2.688-0.33 29.324-0.53 29.324-0.53z m-63.056 80L368 188.222V144zM304 312a40.076 40.076 0 0 1 32-39.2v78.4a40.076 40.076 0 0 1-32-39.2z m64 114.66v-166.882L425.888 144h172.224L656 259.778v166.882a48.342 48.342 0 0 1-9.6 28.8s-65.536 87.308-66.4 88.286A47.964 47.964 0 0 1 544 560h-64a48 48 0 0 1-36.04-16.306c-0.822-0.926-66.354-88.228-66.354-88.228A48.35 48.35 0 0 1 368 426.66z m144 284c-36.948 0-83.13-17.29-107.694-64.73A73.42 73.42 0 0 0 432 588.22v-12.242A79.974 79.974 0 0 0 480 592h64a80.018 80.018 0 0 0 48-16.014v12.234a73.79 73.79 0 0 0 27.712 57.676C595.152 693.36 548.956 710.66 512 710.66z" p-id="17468"></path><path d="M512 784c-69.65 0-97.6-39.308-98.76-40.982l-26.552 17.858C388.188 763.126 424.444 816 512 816s123.812-52.874 125.312-55.124l-26.624-17.752C610.414 743.534 582.622 784 512 784zM240 899.43c-19.884-20.152-29.754-54.956-32.2-69.98l-31.6 5.078c0.546 3.426 13.22 79.016 63.8 104.214V992h32v-176h-32zM784 899.43V816h-32v176h32v-53.258c50.578-25.2 63.252-100.788 63.8-104.214l-31.6-5.078c-2.446 15.024-12.316 49.828-32.2 69.98zM456 308a28 28 0 1 0-28 28 28.03 28.03 0 0 0 28-28zM596 336a28 28 0 1 0-28-28 28.03 28.03 0 0 0 28 28zM496 320h32v64h-32zM565.738 446.936l-11.476-29.872c-0.364 0.142-36.8 13.942-72.568 10.14l-3.388 31.82a152.936 152.936 0 0 0 16.2 0.834 220.662 220.662 0 0 0 71.232-12.922z" p-id="17469"></path></svg>
|