byt-lingxiao-ai 0.3.0 → 0.3.3
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/components/AiMessage.vue +39 -21
- package/components/ChatAvatar.vue +8 -1
- package/components/ChatMessageList.vue +29 -10
- package/components/ChatRobot.vue +5 -0
- package/components/ChatWindow.vue +115 -5
- package/components/ChatWindowHeader.vue +3 -0
- package/components/assets/arrow.png +0 -0
- package/components/assets/empty.png +0 -0
- package/components/assets/entering.png +0 -0
- package/components/assets/logo.png +0 -0
- package/components/assets/normal.png +0 -0
- package/components/assets/output.png +0 -0
- package/components/assets/speaking.png +0 -0
- package/components/assets/think.png +0 -0
- package/components/assets/thinking.png +0 -0
- package/components/assets/waiting.png +0 -0
- package/components/config/index.js +4 -0
- package/components/mixins/messageMixin.js +11 -5
- package/dist/img/empty.f36cb82e.png +0 -0
- package/dist/img/entering.4ef198fb.png +0 -0
- package/dist/img/normal.30197a82.png +0 -0
- package/dist/img/output.1dfa94eb.png +0 -0
- package/dist/img/speaking.fa87fedb.png +0 -0
- package/dist/img/thinking.21ad5ca5.png +0 -0
- package/dist/img/waiting.460478ef.png +0 -0
- package/dist/index.common.js +222 -89
- package/dist/index.common.js.map +1 -1
- package/dist/index.css +2 -2
- package/dist/index.umd.js +222 -89
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/components/assets/byt.mp3 +0 -0
- package/dist/img/entering.42f05909.png +0 -0
- package/dist/img/normal.13f08ecb.png +0 -0
- package/dist/img/output.85c6bd8b.png +0 -0
- package/dist/img/speaking.3ce8b666.png +0 -0
- package/dist/img/thinking.05f29a84.png +0 -0
- package/dist/img/waiting.ac21d76e.png +0 -0
package/components/AiMessage.vue
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="chat-window-message-ai">
|
|
3
3
|
<div class="ai-render">
|
|
4
|
-
<div class="ai-loading" v-if="
|
|
4
|
+
<div class="ai-loading" v-if="isLoading">
|
|
5
5
|
<div class="dot"></div>
|
|
6
6
|
<div class="dot"></div>
|
|
7
7
|
<div class="dot"></div>
|
|
8
8
|
</div>
|
|
9
|
-
<div class="ai-thinking" @click="$emit('thinking-
|
|
10
|
-
<div class="ai-thinking-time"
|
|
11
|
-
<div class="ai-thinking-content" v-if="
|
|
9
|
+
<div class="ai-thinking" @click="$emit('thinking-toggle')" v-if="message.thinking">
|
|
10
|
+
<div class="ai-thinking-time">{{ message.time ? `思考用时${message.time}秒` : '思考中...' }}</div>
|
|
11
|
+
<div class="ai-thinking-content" v-if="thinkingExpanded">{{ message.thinking }}</div>
|
|
12
12
|
</div>
|
|
13
13
|
<div class="ai-content markdown-body" v-html="renderedContent"></div>
|
|
14
14
|
</div>
|
|
@@ -30,7 +30,6 @@ marked.setOptions({
|
|
|
30
30
|
breaks: true,
|
|
31
31
|
gfm: true
|
|
32
32
|
});
|
|
33
|
-
|
|
34
33
|
function parseMarkdown(text) {
|
|
35
34
|
if (!text) return '';
|
|
36
35
|
let html = text;
|
|
@@ -58,18 +57,22 @@ function parseMarkdown(text) {
|
|
|
58
57
|
html = html.replace(/^\s*\d+\.\s+(.+)$/gm, '<li>$1</li>');
|
|
59
58
|
|
|
60
59
|
html = html.replace(/^>\s+(.+)$/gm, '<blockquote>$1</blockquote>');
|
|
61
|
-
|
|
60
|
+
// 解析Markdown链接
|
|
62
61
|
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
|
|
63
62
|
|
|
64
63
|
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" />');
|
|
65
64
|
|
|
66
65
|
html = html.replace(/^-{3,}$/gm, '<hr>');
|
|
66
|
+
// 解析简单URL
|
|
67
|
+
const simpleUrlRegex = /(https?:\/\/[^\s<>"']+)/g;
|
|
68
|
+
html = html.replace(simpleUrlRegex, (match) => {
|
|
69
|
+
return `<a href="${match}" aria-current="page">${match}</a>`;
|
|
70
|
+
});
|
|
67
71
|
|
|
68
|
-
html = html.replace(/\n/g, '<br>');
|
|
72
|
+
// html = html.replace(/\n/g, '<br>');
|
|
69
73
|
|
|
70
74
|
return html;
|
|
71
75
|
}
|
|
72
|
-
|
|
73
76
|
// 解析表格
|
|
74
77
|
function parseTable(text) {
|
|
75
78
|
const lines = text.split('\n');
|
|
@@ -108,7 +111,6 @@ function parseTable(text) {
|
|
|
108
111
|
|
|
109
112
|
return result.join('\n');
|
|
110
113
|
}
|
|
111
|
-
|
|
112
114
|
// 渲染表格
|
|
113
115
|
function renderTable(rows) {
|
|
114
116
|
if (rows.length < 2) return rows.join('\n');
|
|
@@ -130,7 +132,7 @@ function renderTable(rows) {
|
|
|
130
132
|
if (cells.length > 0) {
|
|
131
133
|
html += '<tr>';
|
|
132
134
|
cells.forEach(cell => {
|
|
133
|
-
html += `<td>${cell.trim()}</td>`;
|
|
135
|
+
html += `<td><div class="table-cell">${cell.trim()}</div></td>`;
|
|
134
136
|
});
|
|
135
137
|
html += '</tr>';
|
|
136
138
|
}
|
|
@@ -140,7 +142,6 @@ function renderTable(rows) {
|
|
|
140
142
|
html += '</table></div>';
|
|
141
143
|
return html;
|
|
142
144
|
}
|
|
143
|
-
|
|
144
145
|
// HTML 转义
|
|
145
146
|
function escapeHtml(text) {
|
|
146
147
|
const map = {
|
|
@@ -159,19 +160,17 @@ export default {
|
|
|
159
160
|
message: {
|
|
160
161
|
type: Object,
|
|
161
162
|
required: true
|
|
162
|
-
},
|
|
163
|
-
thinkStatus: {
|
|
164
|
-
type: Boolean,
|
|
165
|
-
default: true
|
|
166
|
-
},
|
|
167
|
-
loading: {
|
|
168
|
-
type: Boolean,
|
|
169
|
-
default: false
|
|
170
163
|
}
|
|
171
164
|
},
|
|
172
165
|
computed: {
|
|
166
|
+
thinkingExpanded() {
|
|
167
|
+
return this.message.thinkingExpanded !== false;
|
|
168
|
+
},
|
|
173
169
|
renderedContent() {
|
|
174
170
|
return parseMarkdown(this.message.content);
|
|
171
|
+
},
|
|
172
|
+
isLoading() {
|
|
173
|
+
return this.message.loading === true;
|
|
175
174
|
}
|
|
176
175
|
},
|
|
177
176
|
watch: {
|
|
@@ -371,16 +370,35 @@ export default {
|
|
|
371
370
|
.markdown-body ::v-deep .table-wrapper{
|
|
372
371
|
overflow-x: auto;
|
|
373
372
|
border: 1px solid #dfe2e5;
|
|
373
|
+
margin: 12px 0;
|
|
374
374
|
}
|
|
375
375
|
.markdown-body ::v-deep .markdown-table {
|
|
376
376
|
border-collapse: collapse;
|
|
377
377
|
width: 100%;
|
|
378
|
-
margin: 12px 0;
|
|
379
378
|
font-size: 14px;
|
|
380
379
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
381
380
|
overflow: hidden;
|
|
381
|
+
margin: -1px 0 0 -1px;
|
|
382
|
+
}
|
|
383
|
+
/* 定义table的滚动条 */
|
|
384
|
+
.markdown-body ::v-deep .table-wrapper::-webkit-scrollbar {
|
|
385
|
+
height: 6px;
|
|
386
|
+
}
|
|
387
|
+
.markdown-body ::v-deep .table-wrapper::-webkit-scrollbar-track {
|
|
388
|
+
background-color: #f3f4f6;
|
|
389
|
+
}
|
|
390
|
+
.markdown-body ::v-deep .table-wrapper::-webkit-scrollbar-thumb {
|
|
391
|
+
background-color: #d1d5db;
|
|
392
|
+
border-radius: 3px;
|
|
393
|
+
}
|
|
394
|
+
.markdown-body ::v-deep .table-wrapper::-webkit-scrollbar-thumb:hover {
|
|
395
|
+
background-color: #9ca3af;
|
|
382
396
|
}
|
|
383
397
|
|
|
398
|
+
.markdown-body ::v-deep .table-cell {
|
|
399
|
+
min-width: 100px;
|
|
400
|
+
text-align: left;
|
|
401
|
+
}
|
|
384
402
|
.markdown-body ::v-deep .markdown-table th,
|
|
385
403
|
.markdown-body ::v-deep .markdown-table td {
|
|
386
404
|
border: 1px solid #dfe2e5;
|
|
@@ -459,6 +477,6 @@ export default {
|
|
|
459
477
|
}
|
|
460
478
|
|
|
461
479
|
.markdown-body ::v-deep br {
|
|
462
|
-
line-height:
|
|
480
|
+
line-height: 0.8em;
|
|
463
481
|
}
|
|
464
482
|
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="chat-ai" @
|
|
2
|
+
<div class="chat-ai" @mousedown="$emit('mousedown')">
|
|
3
3
|
<div :class="['chat-ai-avater', status]"></div>
|
|
4
4
|
<div class="chat-ai-text">{{ statusText }}</div>
|
|
5
5
|
</div>
|
|
@@ -41,6 +41,13 @@ export default {
|
|
|
41
41
|
box-shadow: 0 2px 11.6px 0 rgba(0, 0, 0, 0.1);
|
|
42
42
|
cursor: pointer;
|
|
43
43
|
user-select: none;
|
|
44
|
+
transition: box-shadow 0.2s;
|
|
45
|
+
}
|
|
46
|
+
.chat-ai:hover {
|
|
47
|
+
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.2);
|
|
48
|
+
}
|
|
49
|
+
.chat-ai:active {
|
|
50
|
+
cursor: grabbing;
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
.chat-ai-avater {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="chatArea" class="chat-window-content scrollbar-hide">
|
|
3
|
+
<div v-if="messages.length === 0" class="chat-window-empty"></div>
|
|
3
4
|
<div
|
|
4
5
|
class="chat-window-message"
|
|
5
6
|
v-for="message in messages"
|
|
@@ -13,8 +14,7 @@
|
|
|
13
14
|
v-else
|
|
14
15
|
:message="message"
|
|
15
16
|
:think-status="thinkStatus"
|
|
16
|
-
|
|
17
|
-
@thinking-click="$emit('thinking-click')"
|
|
17
|
+
@thinking-toggle="handleThinkingToggle(message)"
|
|
18
18
|
/>
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
@@ -38,13 +38,22 @@ export default {
|
|
|
38
38
|
thinkStatus: {
|
|
39
39
|
type: Boolean,
|
|
40
40
|
default: true
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
computed: {
|
|
44
|
+
lastMessageObject() {
|
|
45
|
+
const len = this.messages.length;
|
|
46
|
+
if (len === 0) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return this.messages[len - 1];
|
|
45
50
|
}
|
|
46
51
|
},
|
|
47
52
|
methods: {
|
|
53
|
+
handleThinkingToggle(message) {
|
|
54
|
+
console.log('handleThinkingToggle', message)
|
|
55
|
+
this.$set(message, 'thinkingExpanded', !message.thinkingExpanded);
|
|
56
|
+
},
|
|
48
57
|
scrollToBottom() {
|
|
49
58
|
this.$nextTick(() => {
|
|
50
59
|
const chatArea = this.$refs.chatArea
|
|
@@ -55,17 +64,27 @@ export default {
|
|
|
55
64
|
}
|
|
56
65
|
},
|
|
57
66
|
watch: {
|
|
58
|
-
|
|
59
|
-
handler() {
|
|
60
|
-
|
|
67
|
+
lastMessageObject: {
|
|
68
|
+
handler(newMsg) {
|
|
69
|
+
if (newMsg) {
|
|
70
|
+
this.scrollToBottom();
|
|
71
|
+
}
|
|
61
72
|
},
|
|
62
|
-
deep: true
|
|
73
|
+
deep: true,
|
|
74
|
+
immediate: true
|
|
63
75
|
}
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
78
|
</script>
|
|
67
79
|
|
|
68
80
|
<style scoped>
|
|
81
|
+
.chat-window-empty{
|
|
82
|
+
width: 300px;
|
|
83
|
+
height: 300px;
|
|
84
|
+
background: url('./assets/empty.png') no-repeat;
|
|
85
|
+
background-size: cover;
|
|
86
|
+
margin: auto;
|
|
87
|
+
}
|
|
69
88
|
.chat-window-content {
|
|
70
89
|
flex: 1;
|
|
71
90
|
padding: 16px;
|
package/components/ChatRobot.vue
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="chat"
|
|
4
|
+
:style="chatStyle"
|
|
5
|
+
>
|
|
3
6
|
<!-- 隐藏的音频播放器 -->
|
|
4
7
|
<audio
|
|
5
8
|
ref="audioPlayer"
|
|
@@ -21,7 +24,7 @@
|
|
|
21
24
|
<ChatAvatar
|
|
22
25
|
v-else
|
|
23
26
|
:status="avaterStatus"
|
|
24
|
-
@
|
|
27
|
+
@mousedown="startDrag"
|
|
25
28
|
/>
|
|
26
29
|
|
|
27
30
|
<!-- 聊天窗口 -->
|
|
@@ -30,7 +33,6 @@
|
|
|
30
33
|
:messages="messages"
|
|
31
34
|
:input-message="inputMessage"
|
|
32
35
|
:think-status="thinkStatus"
|
|
33
|
-
:loading="isLoading"
|
|
34
36
|
@update:inputMessage="inputMessage = $event"
|
|
35
37
|
@send="handleSend"
|
|
36
38
|
@thinking-click="handleThinkingClick"
|
|
@@ -77,7 +79,31 @@ export default {
|
|
|
77
79
|
thinkStatus: true,
|
|
78
80
|
jumpedTimePoints: new Set(),
|
|
79
81
|
SAMPLE_RATE,
|
|
80
|
-
FRAME_SIZE
|
|
82
|
+
FRAME_SIZE,
|
|
83
|
+
dragThreshold: 5, // 拖拽阈值
|
|
84
|
+
isDragging: false,
|
|
85
|
+
dragStartX: 0,
|
|
86
|
+
dragStartY: 0,
|
|
87
|
+
currentX: 10,
|
|
88
|
+
currentY: 20,
|
|
89
|
+
initialX: 10,
|
|
90
|
+
initialY: 20,
|
|
91
|
+
hasMoved: false,
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
computed: {
|
|
95
|
+
chatStyle() {
|
|
96
|
+
if (this.robotStatus === 'leaving' && !this.visible) {
|
|
97
|
+
return {
|
|
98
|
+
right: `${this.currentX}px`,
|
|
99
|
+
bottom: `${this.currentY}px`,
|
|
100
|
+
cursor: this.isDragging ? 'grabbing' : 'grab',
|
|
101
|
+
transition: this.isDragging ? 'none' : 'right 0.3s ease, bottom 0.3s ease',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
cursor: 'pointer',
|
|
106
|
+
};
|
|
81
107
|
}
|
|
82
108
|
},
|
|
83
109
|
mounted() {
|
|
@@ -85,6 +111,13 @@ export default {
|
|
|
85
111
|
if (this.appendToBody) {
|
|
86
112
|
this.appendToBodyHandler()
|
|
87
113
|
}
|
|
114
|
+
|
|
115
|
+
this.$nextTick(() => {
|
|
116
|
+
const chatEl = this.$el;
|
|
117
|
+
const style = window.getComputedStyle(chatEl);
|
|
118
|
+
this.initialX = this.currentX = parseInt(style.right, 10) || 10;
|
|
119
|
+
this.initialY = this.currentY = parseInt(style.bottom, 10) || 20;
|
|
120
|
+
})
|
|
88
121
|
},
|
|
89
122
|
beforeDestroy() {
|
|
90
123
|
if (this.appendToBody && this.$el.parentElement === document.body) {
|
|
@@ -92,10 +125,80 @@ export default {
|
|
|
92
125
|
}
|
|
93
126
|
this.closeWebSocket()
|
|
94
127
|
this.stopRecording()
|
|
128
|
+
|
|
129
|
+
// 移除全局事件监听器
|
|
130
|
+
document.removeEventListener('mousemove', this.onDrag)
|
|
131
|
+
document.removeEventListener('mouseup', this.stopDrag)
|
|
95
132
|
},
|
|
96
133
|
methods: {
|
|
97
134
|
toggleWindow() {
|
|
98
135
|
this.visible = !this.visible
|
|
136
|
+
|
|
137
|
+
if (this.visible) {
|
|
138
|
+
this.currentX = this.initialX
|
|
139
|
+
this.currentY = this.initialY
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
startDrag() {
|
|
143
|
+
console.log('startDrag')
|
|
144
|
+
if (this.robotStatus !== 'leaving' && this.visible) return;
|
|
145
|
+
|
|
146
|
+
this.isDragging = true;
|
|
147
|
+
this.hasMoved = false;
|
|
148
|
+
|
|
149
|
+
// 记录鼠标的初始位置
|
|
150
|
+
this.dragStartX = event.clientX;
|
|
151
|
+
this.dragStartY = event.clientY;
|
|
152
|
+
|
|
153
|
+
// 绑定全局事件监听器
|
|
154
|
+
document.addEventListener('mousemove', this.onDrag);
|
|
155
|
+
document.addEventListener('mouseup', this.stopDrag);
|
|
156
|
+
},
|
|
157
|
+
onDrag(event) {
|
|
158
|
+
if (!this.isDragging) return;
|
|
159
|
+
|
|
160
|
+
// 鼠标位移量
|
|
161
|
+
const deltaX = event.clientX - this.dragStartX;
|
|
162
|
+
const deltaY = event.clientY - this.dragStartY;
|
|
163
|
+
|
|
164
|
+
if (Math.abs(deltaX) > this.dragThreshold || Math.abs(deltaY) > this.dragThreshold) {
|
|
165
|
+
console.log('移动超过阈值')
|
|
166
|
+
this.hasMoved = true; // 只要移动超过阈值,就标记为拖拽
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 获取 .chat 容器的尺寸
|
|
170
|
+
const chatEl = this.$el;
|
|
171
|
+
const chatWidth = chatEl.offsetWidth;
|
|
172
|
+
const chatHeight = chatEl.offsetHeight;
|
|
173
|
+
|
|
174
|
+
let newX = this.currentX - deltaX;
|
|
175
|
+
let newY = this.currentY - deltaY;
|
|
176
|
+
|
|
177
|
+
// 视口宽度和高度
|
|
178
|
+
const viewportWidth = window.innerWidth;
|
|
179
|
+
const viewportHeight = window.innerHeight;
|
|
180
|
+
|
|
181
|
+
newX = Math.max(0, newX);
|
|
182
|
+
newX = Math.min(viewportWidth - chatWidth, newX);
|
|
183
|
+
|
|
184
|
+
newY = Math.max(0, newY);
|
|
185
|
+
newY = Math.min(viewportHeight - chatHeight, newY);
|
|
186
|
+
|
|
187
|
+
// 更新位置
|
|
188
|
+
this.currentX = newX;
|
|
189
|
+
this.currentY = newY;
|
|
190
|
+
|
|
191
|
+
// 重新设置新的拖拽起始点,实现平滑拖拽
|
|
192
|
+
this.dragStartX = event.clientX;
|
|
193
|
+
this.dragStartY = event.clientY;
|
|
194
|
+
},
|
|
195
|
+
stopDrag() {
|
|
196
|
+
this.isDragging = false;
|
|
197
|
+
document.removeEventListener('mousemove', this.onDrag);
|
|
198
|
+
document.removeEventListener('mouseup', this.stopDrag);
|
|
199
|
+
if (!this.hasMoved) {
|
|
200
|
+
this.toggleWindow()
|
|
201
|
+
}
|
|
99
202
|
},
|
|
100
203
|
handleThinkingClick() {
|
|
101
204
|
this.thinkStatus = !this.thinkStatus
|
|
@@ -123,8 +226,14 @@ export default {
|
|
|
123
226
|
.then(response => response.json())
|
|
124
227
|
.then(data => {
|
|
125
228
|
console.log('时间跳转点:', data)
|
|
229
|
+
console.log('当前时间:', currentTime)
|
|
126
230
|
data.forEach(point => {
|
|
127
|
-
|
|
231
|
+
console.log('跳转点:', point)
|
|
232
|
+
console.log('跳转时间:', point.time)
|
|
233
|
+
console.log('当前时间:', currentTime)
|
|
234
|
+
console.log('是否已跳转:', this.jumpedTimePoints.has(point.time))
|
|
235
|
+
console.log('跳转时间范围:', currentTime >= point.time && currentTime < point.time + 1)
|
|
236
|
+
if (currentTime >= point.time && currentTime < point.time + 1 && !this.jumpedTimePoints.has(point.time)) {
|
|
128
237
|
this.jumpedTimePoints.add(point.time)
|
|
129
238
|
this.$appOptions.store.dispatch('tags/addTagview', {
|
|
130
239
|
path: point.url,
|
|
@@ -143,6 +252,7 @@ export default {
|
|
|
143
252
|
},
|
|
144
253
|
onAudioEnded() {
|
|
145
254
|
this.robotStatus = 'leaving'
|
|
255
|
+
this.avaterStatus = 'normal'
|
|
146
256
|
this.jumpedTimePoints.clear()
|
|
147
257
|
}
|
|
148
258
|
}
|
|
@@ -41,6 +41,8 @@ export default {
|
|
|
41
41
|
name: 'ChatWindowHeader',
|
|
42
42
|
methods: {
|
|
43
43
|
handleOpen() {
|
|
44
|
+
// const chatId = ''
|
|
45
|
+
// const baseUrl = window.location.protocol + '//' + window.location.hostname + ':3100/c/' + chatId;
|
|
44
46
|
const baseUrl = window.location.protocol + '//' + window.location.hostname + ':3100/';
|
|
45
47
|
window.open(baseUrl, '_blank')
|
|
46
48
|
}
|
|
@@ -56,6 +58,7 @@ export default {
|
|
|
56
58
|
background: #fff;
|
|
57
59
|
padding: 16px;
|
|
58
60
|
flex-shrink: 0;
|
|
61
|
+
user-select: none;
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
.chat-window-header-title {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const baseUrl = window.location.protocol + '//' + window.location.hostname;
|
|
2
|
+
const chatPort = '3100';
|
|
3
|
+
|
|
4
|
+
console.log(baseUrl, chatPort);
|
|
1
5
|
export const API_URL = 'http://192.168.8.87:3100/lingxiao-byt/api/v1/mcp/ask';
|
|
2
6
|
export const WS_URL = 'ws://192.168.8.9:9999/ai_model/ws/voice-stream';
|
|
3
7
|
export const AUDIO_URL = '/minio/lingxiaoai/byt.mp3';
|
|
@@ -4,8 +4,7 @@ import { API_URL } from '../config/index.js'
|
|
|
4
4
|
export default {
|
|
5
5
|
data() {
|
|
6
6
|
return {
|
|
7
|
-
streamParser: null
|
|
8
|
-
isLoading: false
|
|
7
|
+
streamParser: null
|
|
9
8
|
}
|
|
10
9
|
},
|
|
11
10
|
|
|
@@ -15,7 +14,6 @@ export default {
|
|
|
15
14
|
updateInterval: 16, // 约60fps
|
|
16
15
|
debug: process.env.NODE_ENV === 'development'
|
|
17
16
|
});
|
|
18
|
-
this.isLoading = true;
|
|
19
17
|
},
|
|
20
18
|
|
|
21
19
|
methods: {
|
|
@@ -28,6 +26,8 @@ export default {
|
|
|
28
26
|
thinking: '',
|
|
29
27
|
charts: [],
|
|
30
28
|
content: '',
|
|
29
|
+
loading: true,
|
|
30
|
+
thinkingExpanded: true
|
|
31
31
|
};
|
|
32
32
|
this.messages.push(message);
|
|
33
33
|
this.currentMessage = message;
|
|
@@ -95,7 +95,6 @@ export default {
|
|
|
95
95
|
|
|
96
96
|
console.log(`流处理完成,总耗时: ${duration}ms`);
|
|
97
97
|
this.avaterStatus = 'normal';
|
|
98
|
-
this.isLoading = false;
|
|
99
98
|
|
|
100
99
|
} catch (error) {
|
|
101
100
|
console.error('发送消息失败:', error);
|
|
@@ -103,7 +102,11 @@ export default {
|
|
|
103
102
|
this.currentMessage.content = '抱歉,发生了错误,请重试。';
|
|
104
103
|
this.$forceUpdate();
|
|
105
104
|
}
|
|
106
|
-
|
|
105
|
+
} finally {
|
|
106
|
+
// 确保加载状态关闭
|
|
107
|
+
if (this.currentMessage) {
|
|
108
|
+
this.currentMessage.loading = false;
|
|
109
|
+
}
|
|
107
110
|
}
|
|
108
111
|
},
|
|
109
112
|
|
|
@@ -141,6 +144,9 @@ export default {
|
|
|
141
144
|
|
|
142
145
|
console.log('收到更新:', result);
|
|
143
146
|
|
|
147
|
+
if (this.currentMessage.loading) {
|
|
148
|
+
this.currentMessage.loading = false;
|
|
149
|
+
}
|
|
144
150
|
// 更新思考内容
|
|
145
151
|
if (result.thinking) {
|
|
146
152
|
this.currentMessage.thinking += result.thinking;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|