qqsl-agent 0.0.1
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/README.md +24 -0
- package/assets/images/card/copy.png +0 -0
- package/assets/images/card/refresh.png +0 -0
- package/assets/images/card/share.png +0 -0
- package/assets/images/card/think-finish.png +0 -0
- package/assets/images/input/relate.png +0 -0
- package/assets/images/input/send-gray.png +0 -0
- package/assets/images/input/send.png +0 -0
- package/assets/images/input/toggle-model.png +0 -0
- package/assets/images/logo.png +0 -0
- package/assets/images/sidebar/collapse.png +0 -0
- package/assets/images/sidebar/delete.png +0 -0
- package/assets/images/sidebar/expand.png +0 -0
- package/assets/images/sidebar/history-chat.png +0 -0
- package/assets/images/sidebar/new-chat.png +0 -0
- package/assets/images/sidebar/time.png +0 -0
- package/assets/styles/index.css +1 -0
- package/chat/chat-input/chat-input.component.d.ts +46 -0
- package/chat/chat-messages/chat-messages.component.d.ts +33 -0
- package/chat/chat-sidebar/chat-sidebar.component.d.ts +20 -0
- package/chat/chat-sidebar/history-group/history-group.component.d.ts +18 -0
- package/chat/chat.component.d.ts +34 -0
- package/chat/chat.module.d.ts +28 -0
- package/chat/components/pagination/pagination.component.d.ts +14 -0
- package/chat/index.d.ts +5 -0
- package/chat/models/conversation.model.d.ts +20 -0
- package/chat/models/knowledge-list.model.d.ts +8 -0
- package/chat/models/message.model.d.ts +41 -0
- package/chat/models/model-list.model.d.ts +9 -0
- package/chat/models/send-message.model.d.ts +33 -0
- package/chat/pipes/markdown.pipe.d.ts +8 -0
- package/chat/public-api.d.ts +2 -0
- package/chat/services/http.service.d.ts +13 -0
- package/chat/services/http2.service.d.ts +45 -0
- package/chat/services/markdown-stream.service.d.ts +10 -0
- package/chat/services/sse.service.d.ts +28 -0
- package/chat/store/store.service.d.ts +17 -0
- package/esm2020/chat/chat-input/chat-input.component.mjs +151 -0
- package/esm2020/chat/chat-messages/chat-messages.component.mjs +110 -0
- package/esm2020/chat/chat-sidebar/chat-sidebar.component.mjs +118 -0
- package/esm2020/chat/chat-sidebar/history-group/history-group.component.mjs +124 -0
- package/esm2020/chat/chat.component.mjs +138 -0
- package/esm2020/chat/chat.module.mjs +98 -0
- package/esm2020/chat/components/pagination/pagination.component.mjs +91 -0
- package/esm2020/chat/models/conversation.model.mjs +2 -0
- package/esm2020/chat/models/knowledge-list.model.mjs +2 -0
- package/esm2020/chat/models/message.model.mjs +35 -0
- package/esm2020/chat/models/model-list.model.mjs +2 -0
- package/esm2020/chat/models/send-message.model.mjs +2 -0
- package/esm2020/chat/pipes/markdown.pipe.mjs +24 -0
- package/esm2020/chat/public-api.mjs +3 -0
- package/esm2020/chat/qqsl-agent-chat.mjs +5 -0
- package/esm2020/chat/services/http.service.mjs +68 -0
- package/esm2020/chat/services/http2.service.mjs +101 -0
- package/esm2020/chat/services/markdown-stream.service.mjs +137 -0
- package/esm2020/chat/services/sse.service.mjs +107 -0
- package/esm2020/chat/store/store.service.mjs +38 -0
- package/esm2020/public-api.mjs +5 -0
- package/esm2020/qqsl-agent.mjs +5 -0
- package/fesm2015/qqsl-agent-chat.mjs +1255 -0
- package/fesm2015/qqsl-agent-chat.mjs.map +1 -0
- package/fesm2015/qqsl-agent.mjs +9 -0
- package/fesm2015/qqsl-agent.mjs.map +1 -0
- package/fesm2020/qqsl-agent-chat.mjs +1248 -0
- package/fesm2020/qqsl-agent-chat.mjs.map +1 -0
- package/fesm2020/qqsl-agent.mjs +9 -0
- package/fesm2020/qqsl-agent.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/package.json +45 -0
- package/public-api.d.ts +2 -0
|
@@ -0,0 +1,1255 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable, inject, EventEmitter, Component, ViewChild, Input, Output, Pipe, NgZone, NgModule } from '@angular/core';
|
|
3
|
+
import * as i1$1 from '@angular/common';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import { Marked } from 'marked';
|
|
6
|
+
import { markedHighlight } from 'marked-highlight';
|
|
7
|
+
import hljs from 'highlight.js';
|
|
8
|
+
import DOMPurify from 'dompurify';
|
|
9
|
+
import * as i1 from '@angular/common/http';
|
|
10
|
+
import { HttpHeaders, HttpResponse, HttpClient, HttpParams } from '@angular/common/http';
|
|
11
|
+
import { BehaviorSubject, filter, map } from 'rxjs';
|
|
12
|
+
import { __awaiter } from 'tslib';
|
|
13
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
14
|
+
import * as i2 from 'ng-zorro-antd/core/transition-patch';
|
|
15
|
+
import * as i3 from 'ng-zorro-antd/popover';
|
|
16
|
+
import { NzPopoverModule } from 'ng-zorro-antd/popover';
|
|
17
|
+
import * as i4 from 'ng-zorro-antd/radio';
|
|
18
|
+
import { NzRadioModule } from 'ng-zorro-antd/radio';
|
|
19
|
+
import * as i5 from 'ng-zorro-antd/checkbox';
|
|
20
|
+
import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
|
|
21
|
+
import * as i6 from 'ng-zorro-antd/icon';
|
|
22
|
+
import { NzIconModule } from 'ng-zorro-antd/icon';
|
|
23
|
+
import * as i7 from '@angular/forms';
|
|
24
|
+
import { FormsModule } from '@angular/forms';
|
|
25
|
+
import { NzMessageService } from 'ng-zorro-antd/message';
|
|
26
|
+
import { trigger, state, style, transition, animate } from '@angular/animations';
|
|
27
|
+
import { NzModalService } from 'ng-zorro-antd/modal';
|
|
28
|
+
import { NzAvatarModule } from 'ng-zorro-antd/avatar';
|
|
29
|
+
import { NzSpinModule } from 'ng-zorro-antd/spin';
|
|
30
|
+
import { NzTagModule } from 'ng-zorro-antd/tag';
|
|
31
|
+
import { NzInputModule } from 'ng-zorro-antd/input';
|
|
32
|
+
import { NzButtonModule } from 'ng-zorro-antd/button';
|
|
33
|
+
import { NzListModule } from 'ng-zorro-antd/list';
|
|
34
|
+
import { NzSliderModule } from 'ng-zorro-antd/slider';
|
|
35
|
+
import { NzLayoutModule } from 'ng-zorro-antd/layout';
|
|
36
|
+
import { NzPaginationModule } from 'ng-zorro-antd/pagination';
|
|
37
|
+
|
|
38
|
+
// markdown-stream.service.ts
|
|
39
|
+
class MarkdownStreamService {
|
|
40
|
+
constructor() {
|
|
41
|
+
this.marked = new Marked(
|
|
42
|
+
// 高亮代码
|
|
43
|
+
markedHighlight({
|
|
44
|
+
emptyLangClass: 'hljs',
|
|
45
|
+
langPrefix: 'hljs language-',
|
|
46
|
+
highlight(code, lang, info) {
|
|
47
|
+
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
|
|
48
|
+
return hljs.highlight(code, { language }).value;
|
|
49
|
+
},
|
|
50
|
+
}));
|
|
51
|
+
// 支持 $$...$$ 和 $...$ 数学公式
|
|
52
|
+
// this.marked.use({
|
|
53
|
+
// // 包含从 markdown 创建标记的函数的对象
|
|
54
|
+
// tokenizer: {
|
|
55
|
+
// inlineMath(src: string) {
|
|
56
|
+
// const cap = /^\$([^\$]+)\$/.exec(src);
|
|
57
|
+
// if (cap) {
|
|
58
|
+
// return { type: 'inlineMath', raw: cap[0], text: cap[1] };
|
|
59
|
+
// }
|
|
60
|
+
// },
|
|
61
|
+
// codespan(src: string) {
|
|
62
|
+
// const math = this.inlineMath(src);
|
|
63
|
+
// if (math) return math;
|
|
64
|
+
// return false;
|
|
65
|
+
// }
|
|
66
|
+
// }
|
|
67
|
+
// });
|
|
68
|
+
// marked 17+ 必须使用 extensions 方式注册
|
|
69
|
+
this.marked.use({
|
|
70
|
+
extensions: [
|
|
71
|
+
{
|
|
72
|
+
name: 'inlineMath',
|
|
73
|
+
level: 'inline',
|
|
74
|
+
start(src) {
|
|
75
|
+
return src.indexOf('$'); // 告诉 marked 从哪里开始尝试匹配
|
|
76
|
+
},
|
|
77
|
+
tokenizer(src) {
|
|
78
|
+
// 匹配 $...$,但不能包含换行,且不允许 $$ 开头(留给块级公式)
|
|
79
|
+
const match = src.match(/^\$(?!\$)([^\n$]+?)\$/);
|
|
80
|
+
if (match) {
|
|
81
|
+
return {
|
|
82
|
+
type: 'inlineMath',
|
|
83
|
+
raw: match[0],
|
|
84
|
+
text: match[1].trim(), // 公式内容
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
},
|
|
89
|
+
renderer(token) {
|
|
90
|
+
// 这里直接返回原始文本,后面你可以用 KaTeX / MathJax 渲染
|
|
91
|
+
return `$${token.text}$`;
|
|
92
|
+
// 或者如果你已经全局接管了渲染,可以 return false
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
// 可选:同时支持 $$...$$ 块级公式
|
|
96
|
+
{
|
|
97
|
+
name: 'displayMath',
|
|
98
|
+
level: 'block',
|
|
99
|
+
start(src) {
|
|
100
|
+
return src.indexOf('$$');
|
|
101
|
+
},
|
|
102
|
+
tokenizer(src) {
|
|
103
|
+
const match = src.match(/^\$\$([\s\S]+?)\$\$/);
|
|
104
|
+
if (match) {
|
|
105
|
+
return {
|
|
106
|
+
type: 'displayMath',
|
|
107
|
+
raw: match[0],
|
|
108
|
+
text: match[1].trim(),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return undefined;
|
|
112
|
+
},
|
|
113
|
+
renderer(token) {
|
|
114
|
+
return `$$${token.text}$$`;
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// 关键:增量渲染(流式专用!)
|
|
121
|
+
renderIncremental(markdown) {
|
|
122
|
+
// 防止渲染一半的代码块崩溃
|
|
123
|
+
const safeMarkdown = this.completeCodeBlocks(markdown);
|
|
124
|
+
try {
|
|
125
|
+
let html = this.marked.parse(safeMarkdown);
|
|
126
|
+
html = DOMPurify.sanitize(html); // 安全
|
|
127
|
+
return html;
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
return this.escapeHtml(safeMarkdown);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// 智能补全未闭合的代码块(流式必备!)
|
|
134
|
+
completeCodeBlocks(text) {
|
|
135
|
+
const lines = text.split('\n');
|
|
136
|
+
let inCode = false;
|
|
137
|
+
let fence = '';
|
|
138
|
+
for (let i = 0; i < lines.length; i++) {
|
|
139
|
+
const line = lines[i];
|
|
140
|
+
if (line.trim().match(/^```/)) {
|
|
141
|
+
if (!inCode) {
|
|
142
|
+
fence = line.trim();
|
|
143
|
+
inCode = true;
|
|
144
|
+
}
|
|
145
|
+
else if (line.trim() === '```' || line.trim().startsWith('```')) {
|
|
146
|
+
inCode = false;
|
|
147
|
+
fence = '';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (inCode) {
|
|
152
|
+
text += '\n```';
|
|
153
|
+
}
|
|
154
|
+
return text;
|
|
155
|
+
}
|
|
156
|
+
escapeHtml(text) {
|
|
157
|
+
const div = document.createElement('div');
|
|
158
|
+
div.textContent = text;
|
|
159
|
+
return div.innerHTML;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
MarkdownStreamService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownStreamService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
163
|
+
MarkdownStreamService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownStreamService, providedIn: 'root' });
|
|
164
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownStreamService, decorators: [{
|
|
165
|
+
type: Injectable,
|
|
166
|
+
args: [{ providedIn: 'root' }]
|
|
167
|
+
}], ctorParameters: function () { return []; } });
|
|
168
|
+
|
|
169
|
+
class StoreService {
|
|
170
|
+
constructor() {
|
|
171
|
+
this.token = '';
|
|
172
|
+
this.baseUrl = '';
|
|
173
|
+
this.currentConversationSource = new BehaviorSubject(null);
|
|
174
|
+
this.currentConversation$ = this.currentConversationSource.asObservable();
|
|
175
|
+
}
|
|
176
|
+
setToken(token) {
|
|
177
|
+
this.token = token;
|
|
178
|
+
}
|
|
179
|
+
getToken() {
|
|
180
|
+
return (this.token ||
|
|
181
|
+
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0aWQiOiI2YjBjZjcwNC1hNmE5LTQxZDQtOWRkYy1kZmZmODlkYTZiNWMiLCJ1c2VyX3V1aWQiOjMxNTAsInVzZXJfbmFtZSI6IjEzMDg5MTI3MzcwIiwicmVnaW9uQ29kZSI6IjYzMDEwMDAwMDAwMCIsImF1dGhvcml0aWVzIjpbIldBVEVSX1NVUFBMWV9VU0VSIiwiTkVUV09SS19QUk9UT0NPTF9VU0VSIiwiUE9SVEFMX01BTkFHRV9BRE1JTiIsIkRJR0lUQUxfSVJSSUdBVElPTl9BRE1JTiIsIkVRVUlQTUVOVF9NT0RFTF9BRE1JTiIsIklOU1BFQ1RJT05fVVNFUiIsIkJJTV9CQU5LIiwiREFUQV9DQURBU1RSRV9BRE1JTiIsIklSUklHQVRJT05fR0FURV9VU0VSIiwiV0FURVJfUVVBTElUWV9VU0VSIiwiSVJSSUdBVElPTl9BRE1JTl9VU0VSIiwiQlVER0VUX01BVEVSX0FETUlOIiwiV0FURVJfTkVUV09SS19BRE1JTiIsIklOU1BFQ1RJT05fQURNSU5fVVNFUiIsIkRJR0lUQUxfSVJSSUdBVElPTl9KRyIsIlJPTEVfVVNFUiIsIkRBVEFfQ0VOVEVSX0lDT05fQURNSU4iLCJXQVJOSU5HX1VTRVIiLCJSRVNPVVJDRV9BRE1JTiIsIldBVEVSX1FVQUxJVFlfQURNSU4iLCJCVURHRVRfTUFURVJfVVNFUiIsIkJVSUxEX0NPTlNUUlVDVElPTl9BRE1JTiIsIkNBTENVTEFUSU9OX0RBVEFfQURNSU4iLCJURVhUVVJFX0FETUlOIiwiREFUQV9DRU5URVJfRklMRV9BRE1JTiIsIlNUQVRJQ19NT0RFTF9BRE1JTiIsIkJJT19QTEFOVF9BRE1JTiIsIkJJTV9ERVNJR04iLCJST0xFX1NVUEVSX0FETUlOIiwiTkVUV09SS19NT0RFTF9VU0VSIiwiV0FURVJfUVVPVEFfQURNSU4iLCJEQVRBX0NFTlRFUl9SSVZFUl9MQUtFIiwiREFNX1VTRVIiLCJTVVBQTFlfRFJBSU5BR0VfQURNSU4iLCJSRVNPVVJDRV9VU0VSIiwiQVJHVU1FTlRBVElPTl9VU0VSIiwiTE9HR0lOR19VU0VSIiwiREFUQV9URVJSQUlOX0FETUlOIiwiQlVER0VUX1FVT1RBX1VTRVIiLCJEQVRBX0NFTlRFUl9SRUdJT05fQURNSU4iLCJURVhUVVJFX1VTRVIiLCJEQVRBX0NFTlRFUl9VU0VSIiwiRVFVSVBNRU5UX01PREVMX1VTRVIiLCJEQVRBX0NFTlRFUl9QQU5PUkFNQSIsIkFSR1VNRU5UQVRJT05fQURNSU4iLCJEQVRBX0NFTlRFUl9XQVRFUl9SRVNPVVJDRSIsIkJJTV9QUk9DRURVUkUiLCJNQU5BR0VNRU5UX0FETUlOX1VTRVIiLCJEUklOR1dBVEVSX1VTRVIiLCJXQVRFUl9TVVBQTFlfQURNSU4iLCJMQU5EX1VTRV9BRE1JTiIsIkRBVEFfQ0VOVEVSX0ZJTEVfVVNFUiIsIkRBVEFfQ0VOVEVSX1dBVEVSX1BST0pFQ1QiLCJIWl9TTEFWRSIsIkRvY3VtZW50X1VTRVIiLCJCVURHRVRfUVVPVEFfQURNSU4iLCJJUlJJR0FUSU9OX1VTRVIiLCJORVRXT1JLX01PREVMX0FETUlOIiwiU1VQUExZX0RSQUlOQUdFX1VTRVIiLCJMT0dHSU5HX0FETUlOIiwiRFJJTkdXQVRFUl9BRE1JTl9VU0VSIiwiTkVUV09SS19VU0VSIiwiTU9ERUxfUEFSQU1fQURNSU4iLCJXQVRFUl9RVU9UQV9VU0VSIiwiU1RBVElDX01PREVMX0RFVkVMT1BFUiIsIkRFUEFSVE1FTlRfQURNSU4iLCJEQVRBX0NFTlRFUl9BTk5FWCIsIk5FVFdPUktfQURNSU4iLCJCSU1fRVFVSVBNRU5UIiwiRElHSVRBTF9JUlJJR0FUSU9OX1VTRVIiLCJNQU5BR0VNRU5UX1VTRVIiLCJCVUlMRF9DT05TVFJVQ1RJT05fVVNFUiIsIkRBVEFfQ0VOVEVSX0FETUlOIiwiUklWRVJfTEFLRV9FVkFMVVRJT05fVVNFUiIsIkJJTV9DT1NUIiwiRFJJTkdXQVRFUl9HQVRFX1VTRVIiLCJEQVRBX0NFTlRFUl9XQVRFUl9XQURJTkciLCJORVRXT1JLX1BST1RPQ09MX0FETUlOIiwiQklNX1VTRVIiLCJEQVRBX0NBREFTVFJFX1VTRVIiLCJTVEFUSUNfTU9ERUxfVVNFUiIsIlJPTEVfQURNSU4iLCJXQVRFUl9TVVBQTFlfR0FURV9VU0VSIiwiUklWRVJfTEFLRV9FVkFMVVRJT05fQURNSU4iLCJCSU1fQURNSU4iLCJCSU9fUExBTlRfVVNFUiJdLCJleHAiOjE3NjQwNjA0NzA1MTQsInJvbGVzIjpbIkhaX1NMQVZFIiwiUk9MRV9BRE1JTiIsIkJJT19QTEFOVF9BRE1JTiIsIlJPTEVfVVNFUiIsIkRBVEFfQ0VOVEVSX1VTRVIiLCJEQVRBX0NFTlRFUl9BRE1JTiIsIkRvY3VtZW50X1VTRVIiLCJEQVRBX0NFTlRFUl9BTk5FWCIsIkJJTV9FUVVJUE1FTlQiLCJMT0dHSU5HX0FETUlOIiwiU1VQUExZX0RSQUlOQUdFX1VTRVIiLCJSSVZFUl9MQUtFX0VWQUxVVElPTl9BRE1JTiIsIkRBVEFfQ0VOVEVSX0lDT05fQURNSU4iLCJXQVRFUl9RVUFMSVRZX1VTRVIiLCJXQVRFUl9RVU9UQV9VU0VSIiwiSVJSSUdBVElPTl9BRE1JTl9VU0VSIiwiQlVER0VUX1FVT1RBX0FETUlOIiwiREFUQV9DRU5URVJfRklMRV9VU0VSIiwiQklNX0RFU0lHTiIsIk1BTkFHRU1FTlRfVVNFUiIsIk5FVFdPUktfUFJPVE9DT0xfQURNSU4iLCJTVEFUSUNfTU9ERUxfREVWRUxPUEVSIiwiV0FSTklOR19VU0VSIiwiQklNX1VTRVIiLCJEQVRBX0NFTlRFUl9SSVZFUl9MQUtFIiwiRFJJTkdXQVRFUl9BRE1JTl9VU0VSIiwiQVJHVU1FTlRBVElPTl9VU0VSIiwiREFUQV9DRU5URVJfV0FURVJfUFJPSkVDVCIsIkRBVEFfQ0FEQVNUUkVfQURNSU4iLCJERVBBUlRNRU5UX0FETUlOIiwiTkVUV09SS19VU0VSIiwiU1RBVElDX01PREVMX0FETUlOIiwiQklNX0JBTksiLCJXQVRFUl9TVVBQTFlfQURNSU4iLCJCSU1fQURNSU4iLCJSRVNPVVJDRV9VU0VSIiwiREFUQV9URVJSQUlOX0FETUlOIiwiREFUQV9DRU5URVJfUkVHSU9OX0FETUlOIiwiV0FURVJfU1VQUExZX1VTRVIiLCJESUdJVEFMX0lSUklHQVRJT05fVVNFUiIsIldBVEVSX1FVQUxJVFlfQURNSU4iLCJORVRXT1JLX0FETUlOIiwiTE9HR0lOR19VU0VSIiwiSU5TUEVDVElPTl9BRE1JTl9VU0VSIiwiSVJSSUdBVElPTl9HQVRFX1VTRVIiLCJORVRXT1JLX1BST1RPQ09MX1VTRVIiLCJMQU5EX1VTRV9BRE1JTiIsIkJVSUxEX0NPTlNUUlVDVElPTl9BRE1JTiIsIkRBVEFfQ0VOVEVSX1BBTk9SQU1BIiwiREFUQV9DRU5URVJfV0FURVJfV0FESU5HIiwiQ0FMQ1VMQVRJT05fREFUQV9BRE1JTiIsIkRBVEFfQ0FEQVNUUkVfVVNFUiIsIkJJT19QTEFOVF9VU0VSIiwiTU9ERUxfUEFSQU1fQURNSU4iLCJTVVBQTFlfRFJBSU5BR0VfQURNSU4iLCJFUVVJUE1FTlRfTU9ERUxfQURNSU4iLCJURVhUVVJFX0FETUlOIiwiQlVER0VUX01BVEVSX1VTRVIiLCJXQVRFUl9RVU9UQV9BRE1JTiIsIkRJR0lUQUxfSVJSSUdBVElPTl9KRyIsIldBVEVSX05FVFdPUktfQURNSU4iLCJCSU1fUFJPQ0VEVVJFIiwiRFJJTkdXQVRFUl9VU0VSIiwiV0FURVJfU1VQUExZX0dBVEVfVVNFUiIsIkJVSUxEX0NPTlNUUlVDVElPTl9VU0VSIiwiRElHSVRBTF9JUlJJR0FUSU9OX0FETUlOIiwiSVJSSUdBVElPTl9VU0VSIiwiUk9MRV9TVVBFUl9BRE1JTiIsIkVRVUlQTUVOVF9NT0RFTF9VU0VSIiwiQklNX0NPU1QiLCJEUklOR1dBVEVSX0dBVEVfVVNFUiIsIklOU1BFQ1RJT05fVVNFUiIsIkFSR1VNRU5UQVRJT05fQURNSU4iLCJQT1JUQUxfTUFOQUdFX0FETUlOIiwiVEVYVFVSRV9VU0VSIiwiTUFOQUdFTUVOVF9BRE1JTl9VU0VSIiwiTkVUV09SS19NT0RFTF9BRE1JTiIsIkJVREdFVF9RVU9UQV9VU0VSIiwiQlVER0VUX01BVEVSX0FETUlOIiwiU1RBVElDX01PREVMX1VTRVIiLCJSSVZFUl9MQUtFX0VWQUxVVElPTl9VU0VSIiwiREFNX1VTRVIiLCJSRVNPVVJDRV9BRE1JTiIsIkRBVEFfQ0VOVEVSX1dBVEVSX1JFU09VUkNFIiwiREFUQV9DRU5URVJfRklMRV9BRE1JTiIsIk5FVFdPUktfTU9ERUxfVVNFUiJdLCJyZWFsX25hbWUiOiLnrqHnkIblkZgiLCJwbGF0Zm9ybUNvZGUiOiI2MzAxMDAwMDAwMDAiLCJwbGF0Zm9ybU5hbWUiOiLopb_lroHluIIiLCJ1c2VyTWFyayI6ZmFsc2UsInBob25lIjoiMTMwODkxMjczNzAiLCJlbnYiOiJyZWxlYXNlIiwidG9wUmVnaW9uQ29kZSI6IjYzMDEwMDAwMDAwMCJ9.Bzlz0LC5rbwItBgw6B-dhSKhXnAATXiaL-rR3EHD4wmJ-BpWWrDakYAjzUCtWL4ExkTqSxXIClCztc412o5pT3b9EN7R6WH5namjpxzwozCVXQyBYvjoCrS5UX1_iUkvVMr6WAqiUUzIanVSrmaRpszxyD8S_fLBge971-Cli44J1FQDBY8QN8TiH3v3c5aDWDBJVYPHqe3_zEAeL3dlERgl8pTLAydx54a2QqCVH2xfX_zSzFXJeK2iamIyI8H4cyP0pQrYl2rjSuR7LsjJSJz_QeSQKZlctG8odrBMV5OoHlWC2isNGqszqyp1POX2HyWb-U4qn8KuKKryjACrow');
|
|
182
|
+
}
|
|
183
|
+
setBaseUrl(url) {
|
|
184
|
+
this.baseUrl = url;
|
|
185
|
+
}
|
|
186
|
+
getBaseUrl() {
|
|
187
|
+
return this.baseUrl;
|
|
188
|
+
}
|
|
189
|
+
setCurrentConversation(conversation) {
|
|
190
|
+
this.conversationICode = conversation === null || conversation === void 0 ? void 0 : conversation.iCode;
|
|
191
|
+
this.currentConversationSource.next(conversation);
|
|
192
|
+
}
|
|
193
|
+
getConversationICode() {
|
|
194
|
+
return this.conversationICode;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
StoreService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: StoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
198
|
+
StoreService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: StoreService, providedIn: 'root' });
|
|
199
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: StoreService, decorators: [{
|
|
200
|
+
type: Injectable,
|
|
201
|
+
args: [{ providedIn: 'root' }]
|
|
202
|
+
}] });
|
|
203
|
+
|
|
204
|
+
let HttpService$1 = class HttpService {
|
|
205
|
+
constructor(http, storeService) {
|
|
206
|
+
this.http = http;
|
|
207
|
+
this.storeService = storeService;
|
|
208
|
+
}
|
|
209
|
+
get(url, paramObj) {
|
|
210
|
+
if (paramObj) {
|
|
211
|
+
return this.http
|
|
212
|
+
.get(url, {
|
|
213
|
+
headers: new HttpHeaders({
|
|
214
|
+
'Content-Type': 'application/json',
|
|
215
|
+
Authorization: this.storeService.getToken(),
|
|
216
|
+
}),
|
|
217
|
+
observe: 'response',
|
|
218
|
+
params: paramObj,
|
|
219
|
+
responseType: 'json',
|
|
220
|
+
withCredentials: false,
|
|
221
|
+
})
|
|
222
|
+
.pipe(filter((r) => r instanceof HttpResponse), map((r) => {
|
|
223
|
+
return r.body;
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
return this.http
|
|
227
|
+
.get(url, {
|
|
228
|
+
headers: new HttpHeaders({
|
|
229
|
+
'Content-Type': 'application/json',
|
|
230
|
+
Authorization: this.storeService.getToken(),
|
|
231
|
+
}),
|
|
232
|
+
observe: 'response',
|
|
233
|
+
responseType: 'json',
|
|
234
|
+
withCredentials: false,
|
|
235
|
+
})
|
|
236
|
+
.pipe(filter((r) => r instanceof HttpResponse), map((r) => {
|
|
237
|
+
return r.body;
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
post(url, body, options) {
|
|
241
|
+
// 默认配置
|
|
242
|
+
const defaultOptions = {
|
|
243
|
+
headers: new HttpHeaders({
|
|
244
|
+
'Content-Type': 'application/json',
|
|
245
|
+
Authorization: this.storeService.getToken(),
|
|
246
|
+
}),
|
|
247
|
+
observe: 'response',
|
|
248
|
+
responseType: 'json',
|
|
249
|
+
withCredentials: false,
|
|
250
|
+
};
|
|
251
|
+
// 如果提供了自定义选项,则合并配置
|
|
252
|
+
const httpOptions = options ? Object.assign(Object.assign({}, defaultOptions), options) : defaultOptions;
|
|
253
|
+
// 发送 POST 请求
|
|
254
|
+
return this.http.post(url, body, httpOptions);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
HttpService$1.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService$1, deps: [{ token: i1.HttpClient }, { token: StoreService }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
258
|
+
HttpService$1.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService$1, providedIn: 'root' });
|
|
259
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService$1, decorators: [{
|
|
260
|
+
type: Injectable,
|
|
261
|
+
args: [{
|
|
262
|
+
providedIn: 'root',
|
|
263
|
+
}]
|
|
264
|
+
}], ctorParameters: function () { return [{ type: i1.HttpClient }, { type: StoreService }]; } });
|
|
265
|
+
|
|
266
|
+
class SseService {
|
|
267
|
+
constructor() {
|
|
268
|
+
this.storeService = inject(StoreService);
|
|
269
|
+
this.conversationICodeSource = new BehaviorSubject(null);
|
|
270
|
+
this.conversationICode$ = this.conversationICodeSource.asObservable();
|
|
271
|
+
// 思考内容
|
|
272
|
+
this.thinkingSource = new BehaviorSubject('');
|
|
273
|
+
this.thinking$ = this.thinkingSource.asObservable();
|
|
274
|
+
// 实时流式文本
|
|
275
|
+
this.messageSource = new BehaviorSubject('');
|
|
276
|
+
this.currentMessage$ = this.messageSource.asObservable();
|
|
277
|
+
this.deltaSource = new BehaviorSubject('');
|
|
278
|
+
this.delta$ = this.deltaSource.asObservable();
|
|
279
|
+
this.loadingSource = new BehaviorSubject(false);
|
|
280
|
+
this.loading$ = this.loadingSource.asObservable();
|
|
281
|
+
// 完整消息历史
|
|
282
|
+
this.fullThinking = '';
|
|
283
|
+
this.fullMessage = '';
|
|
284
|
+
// 支持取消(关键!)
|
|
285
|
+
this.controller = new AbortController();
|
|
286
|
+
}
|
|
287
|
+
streamChat(body) {
|
|
288
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
289
|
+
this.loadingSource.next(true);
|
|
290
|
+
this.messageSource.next(''); // 清空
|
|
291
|
+
this.fullMessage = '';
|
|
292
|
+
try {
|
|
293
|
+
const response = yield fetch(`${this.storeService.getBaseUrl()}/api/ChatModel/generate`, {
|
|
294
|
+
method: 'POST',
|
|
295
|
+
headers: {
|
|
296
|
+
'Content-Type': 'application/json',
|
|
297
|
+
Authorization: this.storeService.getToken(),
|
|
298
|
+
Accept: 'text/event-stream',
|
|
299
|
+
},
|
|
300
|
+
body: JSON.stringify(body),
|
|
301
|
+
});
|
|
302
|
+
if (!response.ok || !response.body) {
|
|
303
|
+
throw new Error('网络错误');
|
|
304
|
+
}
|
|
305
|
+
const reader = response.body.getReader();
|
|
306
|
+
const decoder = new TextDecoder('utf-8');
|
|
307
|
+
while (true) {
|
|
308
|
+
const { done, value } = yield reader.read();
|
|
309
|
+
if (done)
|
|
310
|
+
break;
|
|
311
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
312
|
+
const lines = chunk.split('\n\n');
|
|
313
|
+
for (const line of lines) {
|
|
314
|
+
if (line.startsWith('data:')) {
|
|
315
|
+
const jsonStr = line.replaceAll('data:', '');
|
|
316
|
+
try {
|
|
317
|
+
const data = JSON.parse(jsonStr);
|
|
318
|
+
const delta = data.content || '';
|
|
319
|
+
if (delta) {
|
|
320
|
+
// 对话以及问答编码
|
|
321
|
+
if (delta.conversation) {
|
|
322
|
+
this.conversationICodeSource.next(delta);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
// 比如显示“AI 正在思考...”
|
|
326
|
+
if (data.think) {
|
|
327
|
+
this.fullThinking += delta;
|
|
328
|
+
this.thinkingSource.next(this.fullThinking);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
this.fullMessage += delta;
|
|
332
|
+
this.messageSource.next(this.fullMessage); // 实时推送完整内容
|
|
333
|
+
this.deltaSource.next(delta); // 只推本次新增的文字
|
|
334
|
+
}
|
|
335
|
+
if (data.down) {
|
|
336
|
+
// down: true 表示结束
|
|
337
|
+
this.loadingSource.next(false);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
catch (e) {
|
|
341
|
+
console.log('sse error', e);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
if (err.name !== 'AbortError') {
|
|
349
|
+
this.messageSource.next('请求失败:' + err.message);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
finally {
|
|
353
|
+
this.loadingSource.next(false);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
cancel() {
|
|
358
|
+
this.controller.abort();
|
|
359
|
+
this.controller = new AbortController();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
SseService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
363
|
+
SseService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SseService, providedIn: 'root' });
|
|
364
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SseService, decorators: [{
|
|
365
|
+
type: Injectable,
|
|
366
|
+
args: [{
|
|
367
|
+
providedIn: 'root',
|
|
368
|
+
}]
|
|
369
|
+
}] });
|
|
370
|
+
|
|
371
|
+
// chat-input.component.ts
|
|
372
|
+
class ChatInputComponent {
|
|
373
|
+
constructor() {
|
|
374
|
+
this.messages = [];
|
|
375
|
+
this.sendMessage = new EventEmitter();
|
|
376
|
+
this.messageChange = new EventEmitter();
|
|
377
|
+
this.enter = new EventEmitter();
|
|
378
|
+
this.paste = new EventEmitter();
|
|
379
|
+
this.httpService = inject(HttpService$1);
|
|
380
|
+
this.storeService = inject(StoreService);
|
|
381
|
+
this.sseService = inject(SseService);
|
|
382
|
+
this.isEmpty = true;
|
|
383
|
+
this.currentModel = null;
|
|
384
|
+
this.currentKnowledge = [];
|
|
385
|
+
this.modelList = [];
|
|
386
|
+
this.knowledgeList = [];
|
|
387
|
+
this.modelPopoverVisible = false;
|
|
388
|
+
this.knowledgePopoverVisible = false;
|
|
389
|
+
this.deepThink = true;
|
|
390
|
+
}
|
|
391
|
+
ngOnInit() {
|
|
392
|
+
this.getModelList();
|
|
393
|
+
this.getKnowledgeList();
|
|
394
|
+
}
|
|
395
|
+
// 可供交互的模型
|
|
396
|
+
getModelList() {
|
|
397
|
+
this.httpService.get(`${this.storeService.getBaseUrl()}/api/ChatModel/models`).subscribe((res) => {
|
|
398
|
+
this.modelList = res;
|
|
399
|
+
if (res.length) {
|
|
400
|
+
this.currentModel = res[0];
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
// 模型可选的知识库
|
|
405
|
+
getKnowledgeList() {
|
|
406
|
+
this.httpService
|
|
407
|
+
.get(`${this.storeService.getBaseUrl()}/api/ChatModel/knowledges`)
|
|
408
|
+
.subscribe((res) => {
|
|
409
|
+
this.knowledgeList = res;
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
modelVisibleChange(value) {
|
|
413
|
+
this.modelPopoverVisible = value;
|
|
414
|
+
}
|
|
415
|
+
knowledgeVisibleChange(value) {
|
|
416
|
+
this.knowledgePopoverVisible = value;
|
|
417
|
+
}
|
|
418
|
+
knowledgeSelectChange(value) {
|
|
419
|
+
this.currentKnowledge = value;
|
|
420
|
+
}
|
|
421
|
+
toggleDeepThink() {
|
|
422
|
+
this.deepThink = !this.deepThink;
|
|
423
|
+
}
|
|
424
|
+
updateEmptyState() {
|
|
425
|
+
var _a;
|
|
426
|
+
const el = this.editor.nativeElement;
|
|
427
|
+
const text = ((_a = el.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || '';
|
|
428
|
+
const hasOnlyBr = el.innerHTML === '<br>' || el.innerHTML === '';
|
|
429
|
+
this.isEmpty = text === '' && hasOnlyBr;
|
|
430
|
+
}
|
|
431
|
+
handleInput() {
|
|
432
|
+
this.updateEmptyState();
|
|
433
|
+
const html = this.editor.nativeElement.innerHTML;
|
|
434
|
+
const text = this.editor.nativeElement.textContent || '';
|
|
435
|
+
// this.messageChange.emit(text);
|
|
436
|
+
}
|
|
437
|
+
handleEnter(event) {
|
|
438
|
+
if (!event.shiftKey) {
|
|
439
|
+
event.preventDefault();
|
|
440
|
+
// this.enter.emit((event as any).target!.innerText!.replace(/\s+/g, " ").trim());
|
|
441
|
+
this.send();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
handlePaste(event) {
|
|
445
|
+
// this.paste.emit(event);
|
|
446
|
+
setTimeout(() => this.updateEmptyState(), 0);
|
|
447
|
+
}
|
|
448
|
+
clearInput() {
|
|
449
|
+
this.editor.nativeElement.innerHTML = '';
|
|
450
|
+
this.handleInput();
|
|
451
|
+
}
|
|
452
|
+
regenerateSend(msg) {
|
|
453
|
+
var _a, _b;
|
|
454
|
+
// 正在回答中
|
|
455
|
+
if (this.sseService.loadingSource.value) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const conversation = this.storeService.getConversationICode();
|
|
459
|
+
const message = {
|
|
460
|
+
conversation,
|
|
461
|
+
questionICode: msg.iCode,
|
|
462
|
+
model: (_b = (_a = this.currentModel) === null || _a === void 0 ? void 0 : _a.iCode) !== null && _b !== void 0 ? _b : '',
|
|
463
|
+
think: this.deepThink,
|
|
464
|
+
question: msg.question,
|
|
465
|
+
knowledge: !!this.currentKnowledge.length,
|
|
466
|
+
knowledgeList: this.currentKnowledge,
|
|
467
|
+
};
|
|
468
|
+
this.sendMessage.emit({ message, isRegenerate: true });
|
|
469
|
+
}
|
|
470
|
+
send() {
|
|
471
|
+
var _a, _b;
|
|
472
|
+
// 正在回答中
|
|
473
|
+
if (this.sseService.loadingSource.value) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const text = this.editor.nativeElement.textContent || '';
|
|
477
|
+
const conversation = this.storeService.getConversationICode();
|
|
478
|
+
const message = {
|
|
479
|
+
conversation,
|
|
480
|
+
model: (_b = (_a = this.currentModel) === null || _a === void 0 ? void 0 : _a.iCode) !== null && _b !== void 0 ? _b : '',
|
|
481
|
+
think: this.deepThink,
|
|
482
|
+
question: text,
|
|
483
|
+
knowledge: !!this.currentKnowledge.length,
|
|
484
|
+
knowledgeList: this.currentKnowledge,
|
|
485
|
+
};
|
|
486
|
+
this.clearInput();
|
|
487
|
+
this.sendMessage.emit({ message, isRegenerate: false });
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
ChatInputComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
491
|
+
ChatInputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: ChatInputComponent, selector: "ngx-chat-input", inputs: { messages: "messages" }, outputs: { sendMessage: "sendMessage", messageChange: "messageChange", enter: "enter", paste: "paste" }, viewQueries: [{ propertyName: "editor", first: true, predicate: ["editorRef"], descendants: true }, { propertyName: "chatInputWrapper", first: true, predicate: ["chatInputWrapper"], descendants: true }], ngImport: i0, template: "<!-- \u65B0\u804A\u5929\u4F4D\u4E8E\u5C4F\u5E55\u4E2D\u95F4\uFF0C\u53D1\u9001\u6D88\u606F\u4E4B\u540E\u4F4D\u4E8E\u5E95\u90E8 -->\n<div class=\"chat-input-wrapper\" #chatInputWrapper [class.center]=\"!messages.length\">\n <div class=\"new-chat-title\" *ngIf=\"!messages.length\">\n <img src=\"/assets/images/logo.png\" />\n <span>\u4F60\u597D\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u5230\u4F60\uFF1F\u5F00\u59CB\u5BF9\u8BDD\u5427</span>\n </div>\n <div class=\"chat-input\">\n <div\n #editorRef\n class=\"input-creative-editor\"\n contenteditable=\"true\"\n spellcheck=\"false\"\n data-placeholder=\"\u8BF7\u8F93\u5165\u5E76\u53D1\u9001\u6D88\u606F\"\n [attr.data-empty]=\"isEmpty\"\n (input)=\"handleInput()\"\n (keydown.enter)=\"handleEnter($event)\"\n (paste)=\"handlePaste($event)\"\n ></div>\n <div class=\"input-bottom\">\n <div class=\"input-tools\">\n <div class=\"select-tool\" [ngClass]=\"{ 'select-tool-active': deepThink }\" (click)=\"toggleDeepThink()\">\n <svg\n t=\"1764131026949\"\n style=\"width: 14px; height: 14px\"\n class=\"icon\"\n viewBox=\"0 0 1024 1024\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n p-id=\"5012\"\n width=\"64\"\n height=\"64\"\n >\n <path\n d=\"M903.36 512a795.52 795.52 0 0 1 65.536 136.704c48 132.16 39.616 230.528-25.024 295.168-64.64 64.64-163.008 72.96-295.168 24.96A795.52 795.52 0 0 1 512 903.424a795.52 795.52 0 0 1-136.704 65.536c-132.096 48-230.528 39.616-295.168-25.024-64.64-64.64-72.96-163.008-24.96-295.104 16.64-46.016 38.528-91.52 65.536-136.768A795.52 795.52 0 0 1 55.04 375.232C7.04 243.2 15.36 144.768 80 80.128 144.768 15.488 243.2 7.168 375.296 55.04c46.016 16.64 91.584 38.528 136.768 65.536A795.52 795.52 0 0 1 648.768 55.104c132.096-48 230.464-39.616 295.104 25.024 64.64 64.64 72.96 163.008 25.024 295.104A795.52 795.52 0 0 1 903.36 512z m-53.12-79.424c15.168-28.736 27.968-57.536 38.464-86.4 35.584-98.112 33.92-166.656-5.12-205.696-39.04-39.04-107.648-40.768-205.696-5.12a685.44 685.44 0 0 0-86.4 38.4 1240.96 1240.96 0 0 1 138.432 120.32 1240.832 1240.832 0 0 1 120.32 138.496zM432.576 173.824a685.44 685.44 0 0 0-86.4-38.528c-98.112-35.584-166.656-33.92-205.696 5.12-39.04 39.04-40.768 107.648-5.12 205.696 10.432 28.928 23.296 57.728 38.4 86.4a1240.896 1240.896 0 0 1 120.32-138.432 1240.832 1240.832 0 0 1 138.496-120.32zM173.824 591.488a685.44 685.44 0 0 0-38.464 86.4c-35.648 98.048-33.92 166.592 5.12 205.632 39.04 39.04 107.584 40.768 205.696 5.12a685.44 685.44 0 0 0 86.4-38.4 1240.768 1240.768 0 0 1-138.496-120.32 1240.96 1240.96 0 0 1-120.256-138.432z m495.744 78.08A1112.064 1112.064 0 0 0 802.048 512a1112.064 1112.064 0 0 0-132.48-157.568A1112.128 1112.128 0 0 0 512 221.952a1112.128 1112.128 0 0 0-157.504 132.48A1112.064 1112.064 0 0 0 222.016 512a1112.192 1112.192 0 0 0 132.416 157.568A1112.064 1112.064 0 0 0 512 802.048a1112.128 1112.128 0 0 0 157.568-132.48z m-78.08 180.608c28.672 15.168 57.472 28.032 86.4 38.464 98.048 35.648 166.592 33.92 205.632-5.12 39.04-39.04 40.768-107.584 5.12-205.696a685.504 685.504 0 0 0-38.4-86.4 1240.832 1240.832 0 0 1-120.32 138.496 1240.96 1240.96 0 0 1-138.432 120.32zM585.088 512a73.152 73.152 0 1 1-146.24 0 73.152 73.152 0 0 1 146.304 0z\"\n fill=\"currentColor\"\n p-id=\"5013\"\n ></path>\n </svg>\n \u6DF1\u5EA6\u601D\u8003\n </div>\n <div\n class=\"select-tool\"\n nz-popover\n [nzPopoverContent]=\"modelListContent\"\n nzPopoverTrigger=\"click\"\n nzPopoverOverlayClassName=\"params-select-popover\"\n [nzPopoverVisible]=\"modelPopoverVisible\"\n (nzPopoverVisibleChange)=\"modelVisibleChange($event)\"\n >\n <img src=\"/assets/images/input/toggle-model.png\" style=\"width: 12px; height: 12px\" />\n {{ currentModel ? currentModel?.name : '\u9009\u62E9\u6A21\u578B' }}\n </div>\n <div\n class=\"select-tool\"\n nz-popover\n [nzPopoverContent]=\"knowledgeListContent\"\n nzPopoverTrigger=\"click\"\n nzPopoverOverlayClassName=\"params-select-popover\"\n [nzPopoverVisible]=\"knowledgePopoverVisible\"\n (nzPopoverVisibleChange)=\"knowledgeVisibleChange($event)\"\n >\n <img src=\"/assets/images/input/relate.png\" style=\"width: 14px; height: 8px\" />\n \u5173\u8054\u77E5\u8BC6\u5E93\n </div>\n </div>\n <img class=\"send\" src=\"/assets/images/input/send.png\" (click)=\"send()\" [hidden]=\"sseService.loading$ | async\" />\n <div class=\"send-loading\" *ngIf=\"sseService.loading$ | async\">\n <div class=\"send-loading-inner\">\n <svg viewBox=\"0 0 36 36\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" data-icon=\"spin\">\n <defs>\n <linearGradient x1=\"0%\" y1=\"100%\" x2=\"100%\" y2=\"100%\" id=\"linearGradient-1\">\n <stop stop-color=\"currentColor\" stop-opacity=\"0\" offset=\"0%\"></stop>\n <stop stop-color=\"currentColor\" stop-opacity=\"0.50\" offset=\"39.9430698%\"></stop>\n <stop stop-color=\"currentColor\" offset=\"100%\"></stop>\n </linearGradient>\n </defs>\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect fill-opacity=\"0.01\" fill=\"none\" x=\"0\" y=\"0\" width=\"36\" height=\"36\"></rect>\n <path\n d=\"M34,18 C34,9.163444 26.836556,2 18,2 C11.6597233,2 6.18078805,5.68784135 3.59122325,11.0354951\"\n stroke=\"url(#linearGradient-1)\"\n stroke-width=\"4\"\n stroke-linecap=\"round\"\n ></path>\n </g>\n </svg>\n </div>\n </div>\n <!-- <img class=\"send\" src=\"/assets/images/input/send-gray.png\" /> -->\n </div>\n </div>\n <div class=\"chat-input-backdrop\"></div>\n</div>\n\n<ng-template #modelListContent>\n <div style=\"width: 200px\">\n <div class=\"select-title\">\n \u5207\u6362\u6A21\u578B\n <i nz-icon nzType=\"close\" style=\"color: #999\" (click)=\"modelVisibleChange(false)\"></i>\n </div>\n <div class=\"select-content\">\n <nz-radio-group [(ngModel)]=\"currentModel\">\n <label\n nz-radio\n [nzValue]=\"item\"\n [style.margin-top.px]=\"i === 0 ? 0 : 10\"\n *ngFor=\"let item of modelList; let i = index\"\n >\n {{ item.name }}\n </label>\n </nz-radio-group>\n </div>\n </div>\n</ng-template>\n\n<ng-template #knowledgeListContent>\n <div style=\"width: 150px\">\n <div class=\"select-title\">\n \u5173\u8054\u77E5\u8BC6\u5E93\n <i nz-icon nzType=\"close\" style=\"color: #999\" (click)=\"knowledgeVisibleChange(false)\"></i>\n </div>\n <div class=\"select-content\">\n <nz-checkbox-wrapper style=\"width: 100%\" (nzOnChange)=\"knowledgeSelectChange($event)\">\n <div *ngFor=\"let item of knowledgeList\">\n <label nz-checkbox [nzValue]=\"item.iCode\">{{ item.name }}</label>\n </div>\n </nz-checkbox-wrapper>\n </div>\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";.chat-input-wrapper{position:absolute;bottom:0;left:0;right:0;max-width:800px;min-width:350px;margin:0 auto;padding-bottom:40px}.chat-input-wrapper.center{bottom:50%;transform:translateY(50%)}.new-chat-title{padding-bottom:40px;text-align:center;font-weight:500;font-size:24px;color:#000}.new-chat-title img{width:40px;height:32px;margin-right:12px;margin-top:-6px}.chat-input{position:relative;display:flex;flex-direction:column;justify-content:space-between;padding:20px 18px;margin:0 auto;background:#FFFFFF;box-shadow:0 2px 8px #0000001a;border-radius:20px;border:1px solid #D9DADB;z-index:30}.chat-input .input-creative-editor{min-height:60px;max-height:300px;overflow-y:auto;outline:none;box-shadow:none;border:none}.chat-input .input-creative-editor[data-empty=true]:before{content:attr(data-placeholder);color:#9a9b9b}.chat-input .input-bottom{padding-top:16px;display:flex;align-items:center;justify-content:space-between;-webkit-user-select:none;user-select:none}.chat-input .input-bottom .input-tools{display:flex;align-items:center;gap:10px}.chat-input .input-bottom .select-tool{display:flex;align-items:center;justify-content:center;height:36px;padding:0 12px;gap:12px;background:#FFFFFF;border-radius:12px;border:1px solid #DBDCE0;cursor:pointer}.chat-input .input-bottom .select-tool-active{border-color:#b7c8fe;color:#3964fe;background:#edf3fe}.chat-input .input-bottom .send{width:32px;height:32px}::ng-deep .params-select-popover .ant-popover-inner{border-radius:10px}::ng-deep .params-select-popover .select-title{display:flex;align-items:center;justify-content:center;position:relative}::ng-deep .params-select-popover .select-title i{position:absolute;right:0;top:3px;cursor:pointer}::ng-deep .params-select-popover .select-content{max-height:200px;padding:16px 0;overflow-y:auto}.chat-input-backdrop{position:absolute;bottom:0;left:0;width:100%;height:100px;z-index:10;background-image:linear-gradient(to bottom,hsl(0,0%,99%),hsla(0,0%,99%,.8))}.send-loading{min-width:34px;height:34px;border-radius:50%;margin-top:auto;display:flex;flex-shrink:0;align-items:center;flex-direction:column;justify-content:center;cursor:not-allowed;white-space:nowrap;color:#fff;background:#3964fe;transition:background .2s;opacity:.4}.send-loading-inner{width:16px;height:16px}.send-loading svg{will-change:transform;animation:.6s linear infinite send-loading}@keyframes send-loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.ɵNzTransitionPatchDirective, selector: "[nz-button], nz-button-group, [nz-icon], [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i3.NzPopoverDirective, selector: "[nz-popover]", inputs: ["nzPopoverArrowPointAtCenter", "nzPopoverTitle", "nzPopoverContent", "nz-popover", "nzPopoverTrigger", "nzPopoverPlacement", "nzPopoverOrigin", "nzPopoverVisible", "nzPopoverMouseEnterDelay", "nzPopoverMouseLeaveDelay", "nzPopoverOverlayClassName", "nzPopoverOverlayStyle", "nzPopoverBackdrop"], outputs: ["nzPopoverVisibleChange"], exportAs: ["nzPopover"] }, { kind: "component", type: i4.NzRadioComponent, selector: "[nz-radio],[nz-radio-button]", inputs: ["nzValue", "nzDisabled", "nzAutoFocus"], exportAs: ["nzRadio"] }, { kind: "component", type: i4.NzRadioGroupComponent, selector: "nz-radio-group", inputs: ["nzDisabled", "nzButtonStyle", "nzSize", "nzName"], exportAs: ["nzRadioGroup"] }, { kind: "component", type: i5.NzCheckboxComponent, selector: "[nz-checkbox]", inputs: ["nzValue", "nzAutoFocus", "nzDisabled", "nzIndeterminate", "nzChecked", "nzId"], outputs: ["nzCheckedChange"], exportAs: ["nzCheckbox"] }, { kind: "component", type: i5.NzCheckboxWrapperComponent, selector: "nz-checkbox-wrapper", outputs: ["nzOnChange"], exportAs: ["nzCheckboxWrapper"] }, { kind: "directive", type: i6.NzIconDirective, selector: "[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "directive", type: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] });
|
|
492
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatInputComponent, decorators: [{
|
|
493
|
+
type: Component,
|
|
494
|
+
args: [{ selector: 'ngx-chat-input', template: "<!-- \u65B0\u804A\u5929\u4F4D\u4E8E\u5C4F\u5E55\u4E2D\u95F4\uFF0C\u53D1\u9001\u6D88\u606F\u4E4B\u540E\u4F4D\u4E8E\u5E95\u90E8 -->\n<div class=\"chat-input-wrapper\" #chatInputWrapper [class.center]=\"!messages.length\">\n <div class=\"new-chat-title\" *ngIf=\"!messages.length\">\n <img src=\"/assets/images/logo.png\" />\n <span>\u4F60\u597D\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u5230\u4F60\uFF1F\u5F00\u59CB\u5BF9\u8BDD\u5427</span>\n </div>\n <div class=\"chat-input\">\n <div\n #editorRef\n class=\"input-creative-editor\"\n contenteditable=\"true\"\n spellcheck=\"false\"\n data-placeholder=\"\u8BF7\u8F93\u5165\u5E76\u53D1\u9001\u6D88\u606F\"\n [attr.data-empty]=\"isEmpty\"\n (input)=\"handleInput()\"\n (keydown.enter)=\"handleEnter($event)\"\n (paste)=\"handlePaste($event)\"\n ></div>\n <div class=\"input-bottom\">\n <div class=\"input-tools\">\n <div class=\"select-tool\" [ngClass]=\"{ 'select-tool-active': deepThink }\" (click)=\"toggleDeepThink()\">\n <svg\n t=\"1764131026949\"\n style=\"width: 14px; height: 14px\"\n class=\"icon\"\n viewBox=\"0 0 1024 1024\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n p-id=\"5012\"\n width=\"64\"\n height=\"64\"\n >\n <path\n d=\"M903.36 512a795.52 795.52 0 0 1 65.536 136.704c48 132.16 39.616 230.528-25.024 295.168-64.64 64.64-163.008 72.96-295.168 24.96A795.52 795.52 0 0 1 512 903.424a795.52 795.52 0 0 1-136.704 65.536c-132.096 48-230.528 39.616-295.168-25.024-64.64-64.64-72.96-163.008-24.96-295.104 16.64-46.016 38.528-91.52 65.536-136.768A795.52 795.52 0 0 1 55.04 375.232C7.04 243.2 15.36 144.768 80 80.128 144.768 15.488 243.2 7.168 375.296 55.04c46.016 16.64 91.584 38.528 136.768 65.536A795.52 795.52 0 0 1 648.768 55.104c132.096-48 230.464-39.616 295.104 25.024 64.64 64.64 72.96 163.008 25.024 295.104A795.52 795.52 0 0 1 903.36 512z m-53.12-79.424c15.168-28.736 27.968-57.536 38.464-86.4 35.584-98.112 33.92-166.656-5.12-205.696-39.04-39.04-107.648-40.768-205.696-5.12a685.44 685.44 0 0 0-86.4 38.4 1240.96 1240.96 0 0 1 138.432 120.32 1240.832 1240.832 0 0 1 120.32 138.496zM432.576 173.824a685.44 685.44 0 0 0-86.4-38.528c-98.112-35.584-166.656-33.92-205.696 5.12-39.04 39.04-40.768 107.648-5.12 205.696 10.432 28.928 23.296 57.728 38.4 86.4a1240.896 1240.896 0 0 1 120.32-138.432 1240.832 1240.832 0 0 1 138.496-120.32zM173.824 591.488a685.44 685.44 0 0 0-38.464 86.4c-35.648 98.048-33.92 166.592 5.12 205.632 39.04 39.04 107.584 40.768 205.696 5.12a685.44 685.44 0 0 0 86.4-38.4 1240.768 1240.768 0 0 1-138.496-120.32 1240.96 1240.96 0 0 1-120.256-138.432z m495.744 78.08A1112.064 1112.064 0 0 0 802.048 512a1112.064 1112.064 0 0 0-132.48-157.568A1112.128 1112.128 0 0 0 512 221.952a1112.128 1112.128 0 0 0-157.504 132.48A1112.064 1112.064 0 0 0 222.016 512a1112.192 1112.192 0 0 0 132.416 157.568A1112.064 1112.064 0 0 0 512 802.048a1112.128 1112.128 0 0 0 157.568-132.48z m-78.08 180.608c28.672 15.168 57.472 28.032 86.4 38.464 98.048 35.648 166.592 33.92 205.632-5.12 39.04-39.04 40.768-107.584 5.12-205.696a685.504 685.504 0 0 0-38.4-86.4 1240.832 1240.832 0 0 1-120.32 138.496 1240.96 1240.96 0 0 1-138.432 120.32zM585.088 512a73.152 73.152 0 1 1-146.24 0 73.152 73.152 0 0 1 146.304 0z\"\n fill=\"currentColor\"\n p-id=\"5013\"\n ></path>\n </svg>\n \u6DF1\u5EA6\u601D\u8003\n </div>\n <div\n class=\"select-tool\"\n nz-popover\n [nzPopoverContent]=\"modelListContent\"\n nzPopoverTrigger=\"click\"\n nzPopoverOverlayClassName=\"params-select-popover\"\n [nzPopoverVisible]=\"modelPopoverVisible\"\n (nzPopoverVisibleChange)=\"modelVisibleChange($event)\"\n >\n <img src=\"/assets/images/input/toggle-model.png\" style=\"width: 12px; height: 12px\" />\n {{ currentModel ? currentModel?.name : '\u9009\u62E9\u6A21\u578B' }}\n </div>\n <div\n class=\"select-tool\"\n nz-popover\n [nzPopoverContent]=\"knowledgeListContent\"\n nzPopoverTrigger=\"click\"\n nzPopoverOverlayClassName=\"params-select-popover\"\n [nzPopoverVisible]=\"knowledgePopoverVisible\"\n (nzPopoverVisibleChange)=\"knowledgeVisibleChange($event)\"\n >\n <img src=\"/assets/images/input/relate.png\" style=\"width: 14px; height: 8px\" />\n \u5173\u8054\u77E5\u8BC6\u5E93\n </div>\n </div>\n <img class=\"send\" src=\"/assets/images/input/send.png\" (click)=\"send()\" [hidden]=\"sseService.loading$ | async\" />\n <div class=\"send-loading\" *ngIf=\"sseService.loading$ | async\">\n <div class=\"send-loading-inner\">\n <svg viewBox=\"0 0 36 36\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" data-icon=\"spin\">\n <defs>\n <linearGradient x1=\"0%\" y1=\"100%\" x2=\"100%\" y2=\"100%\" id=\"linearGradient-1\">\n <stop stop-color=\"currentColor\" stop-opacity=\"0\" offset=\"0%\"></stop>\n <stop stop-color=\"currentColor\" stop-opacity=\"0.50\" offset=\"39.9430698%\"></stop>\n <stop stop-color=\"currentColor\" offset=\"100%\"></stop>\n </linearGradient>\n </defs>\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect fill-opacity=\"0.01\" fill=\"none\" x=\"0\" y=\"0\" width=\"36\" height=\"36\"></rect>\n <path\n d=\"M34,18 C34,9.163444 26.836556,2 18,2 C11.6597233,2 6.18078805,5.68784135 3.59122325,11.0354951\"\n stroke=\"url(#linearGradient-1)\"\n stroke-width=\"4\"\n stroke-linecap=\"round\"\n ></path>\n </g>\n </svg>\n </div>\n </div>\n <!-- <img class=\"send\" src=\"/assets/images/input/send-gray.png\" /> -->\n </div>\n </div>\n <div class=\"chat-input-backdrop\"></div>\n</div>\n\n<ng-template #modelListContent>\n <div style=\"width: 200px\">\n <div class=\"select-title\">\n \u5207\u6362\u6A21\u578B\n <i nz-icon nzType=\"close\" style=\"color: #999\" (click)=\"modelVisibleChange(false)\"></i>\n </div>\n <div class=\"select-content\">\n <nz-radio-group [(ngModel)]=\"currentModel\">\n <label\n nz-radio\n [nzValue]=\"item\"\n [style.margin-top.px]=\"i === 0 ? 0 : 10\"\n *ngFor=\"let item of modelList; let i = index\"\n >\n {{ item.name }}\n </label>\n </nz-radio-group>\n </div>\n </div>\n</ng-template>\n\n<ng-template #knowledgeListContent>\n <div style=\"width: 150px\">\n <div class=\"select-title\">\n \u5173\u8054\u77E5\u8BC6\u5E93\n <i nz-icon nzType=\"close\" style=\"color: #999\" (click)=\"knowledgeVisibleChange(false)\"></i>\n </div>\n <div class=\"select-content\">\n <nz-checkbox-wrapper style=\"width: 100%\" (nzOnChange)=\"knowledgeSelectChange($event)\">\n <div *ngFor=\"let item of knowledgeList\">\n <label nz-checkbox [nzValue]=\"item.iCode\">{{ item.name }}</label>\n </div>\n </nz-checkbox-wrapper>\n </div>\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";.chat-input-wrapper{position:absolute;bottom:0;left:0;right:0;max-width:800px;min-width:350px;margin:0 auto;padding-bottom:40px}.chat-input-wrapper.center{bottom:50%;transform:translateY(50%)}.new-chat-title{padding-bottom:40px;text-align:center;font-weight:500;font-size:24px;color:#000}.new-chat-title img{width:40px;height:32px;margin-right:12px;margin-top:-6px}.chat-input{position:relative;display:flex;flex-direction:column;justify-content:space-between;padding:20px 18px;margin:0 auto;background:#FFFFFF;box-shadow:0 2px 8px #0000001a;border-radius:20px;border:1px solid #D9DADB;z-index:30}.chat-input .input-creative-editor{min-height:60px;max-height:300px;overflow-y:auto;outline:none;box-shadow:none;border:none}.chat-input .input-creative-editor[data-empty=true]:before{content:attr(data-placeholder);color:#9a9b9b}.chat-input .input-bottom{padding-top:16px;display:flex;align-items:center;justify-content:space-between;-webkit-user-select:none;user-select:none}.chat-input .input-bottom .input-tools{display:flex;align-items:center;gap:10px}.chat-input .input-bottom .select-tool{display:flex;align-items:center;justify-content:center;height:36px;padding:0 12px;gap:12px;background:#FFFFFF;border-radius:12px;border:1px solid #DBDCE0;cursor:pointer}.chat-input .input-bottom .select-tool-active{border-color:#b7c8fe;color:#3964fe;background:#edf3fe}.chat-input .input-bottom .send{width:32px;height:32px}::ng-deep .params-select-popover .ant-popover-inner{border-radius:10px}::ng-deep .params-select-popover .select-title{display:flex;align-items:center;justify-content:center;position:relative}::ng-deep .params-select-popover .select-title i{position:absolute;right:0;top:3px;cursor:pointer}::ng-deep .params-select-popover .select-content{max-height:200px;padding:16px 0;overflow-y:auto}.chat-input-backdrop{position:absolute;bottom:0;left:0;width:100%;height:100px;z-index:10;background-image:linear-gradient(to bottom,hsl(0,0%,99%),hsla(0,0%,99%,.8))}.send-loading{min-width:34px;height:34px;border-radius:50%;margin-top:auto;display:flex;flex-shrink:0;align-items:center;flex-direction:column;justify-content:center;cursor:not-allowed;white-space:nowrap;color:#fff;background:#3964fe;transition:background .2s;opacity:.4}.send-loading-inner{width:16px;height:16px}.send-loading svg{will-change:transform;animation:.6s linear infinite send-loading}@keyframes send-loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
495
|
+
}], propDecorators: { editor: [{
|
|
496
|
+
type: ViewChild,
|
|
497
|
+
args: ['editorRef']
|
|
498
|
+
}], chatInputWrapper: [{
|
|
499
|
+
type: ViewChild,
|
|
500
|
+
args: ['chatInputWrapper']
|
|
501
|
+
}], messages: [{
|
|
502
|
+
type: Input
|
|
503
|
+
}], sendMessage: [{
|
|
504
|
+
type: Output
|
|
505
|
+
}], messageChange: [{
|
|
506
|
+
type: Output
|
|
507
|
+
}], enter: [{
|
|
508
|
+
type: Output
|
|
509
|
+
}], paste: [{
|
|
510
|
+
type: Output
|
|
511
|
+
}] } });
|
|
512
|
+
|
|
513
|
+
class SimplePaginationComponent {
|
|
514
|
+
constructor() {
|
|
515
|
+
/** 当前页码 */
|
|
516
|
+
this.current = 1;
|
|
517
|
+
/** 总页数 */
|
|
518
|
+
this.total = 1;
|
|
519
|
+
/** 当页码改变时触发 */
|
|
520
|
+
this.pageChange = new EventEmitter();
|
|
521
|
+
}
|
|
522
|
+
onPrev() {
|
|
523
|
+
if (this.current > 1) {
|
|
524
|
+
this.pageChange.emit(this.current - 1);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
onNext() {
|
|
528
|
+
if (this.current < this.total) {
|
|
529
|
+
this.pageChange.emit(this.current + 1);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
SimplePaginationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SimplePaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
534
|
+
SimplePaginationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: SimplePaginationComponent, selector: "ngx-simple-pagination", inputs: { current: "current", total: "total" }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: `
|
|
535
|
+
<div class="pagination-container">
|
|
536
|
+
<button
|
|
537
|
+
class="nav-btn"
|
|
538
|
+
[disabled]="current === 1"
|
|
539
|
+
(click)="onPrev()">
|
|
540
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
|
|
541
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
542
|
+
<polyline points="15 18 9 12 15 6"></polyline>
|
|
543
|
+
</svg>
|
|
544
|
+
</button>
|
|
545
|
+
|
|
546
|
+
<span class="page-info">
|
|
547
|
+
<span class="current">{{ current }}</span>
|
|
548
|
+
<span class="separator">/</span>
|
|
549
|
+
<span class="total">{{ total }}</span>
|
|
550
|
+
</span>
|
|
551
|
+
|
|
552
|
+
<button
|
|
553
|
+
class="nav-btn"
|
|
554
|
+
[disabled]="current === total"
|
|
555
|
+
(click)="onNext()">
|
|
556
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
|
|
557
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
558
|
+
<polyline points="9 18 15 12 9 6"></polyline>
|
|
559
|
+
</svg>
|
|
560
|
+
</button>
|
|
561
|
+
</div>
|
|
562
|
+
`, isInline: true, styles: [":host{display:inline-block}.pagination-container{display:flex;align-items:center;gap:8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;-webkit-user-select:none;user-select:none;color:#606266}.nav-btn{background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:#909399;transition:color .2s,background-color .2s;border-radius:4px}.nav-btn:hover:not(:disabled){color:#303133;background-color:#f0f2f5}.nav-btn:disabled{cursor:not-allowed;color:#dcdfe6;opacity:.6}.page-info{font-size:14px;font-weight:500;letter-spacing:1px;color:#606266;display:flex;align-items:center;gap:4px}.separator{color:#909399}\n"] });
|
|
563
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SimplePaginationComponent, decorators: [{
|
|
564
|
+
type: Component,
|
|
565
|
+
args: [{ selector: 'ngx-simple-pagination', template: `
|
|
566
|
+
<div class="pagination-container">
|
|
567
|
+
<button
|
|
568
|
+
class="nav-btn"
|
|
569
|
+
[disabled]="current === 1"
|
|
570
|
+
(click)="onPrev()">
|
|
571
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
|
|
572
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
573
|
+
<polyline points="15 18 9 12 15 6"></polyline>
|
|
574
|
+
</svg>
|
|
575
|
+
</button>
|
|
576
|
+
|
|
577
|
+
<span class="page-info">
|
|
578
|
+
<span class="current">{{ current }}</span>
|
|
579
|
+
<span class="separator">/</span>
|
|
580
|
+
<span class="total">{{ total }}</span>
|
|
581
|
+
</span>
|
|
582
|
+
|
|
583
|
+
<button
|
|
584
|
+
class="nav-btn"
|
|
585
|
+
[disabled]="current === total"
|
|
586
|
+
(click)="onNext()">
|
|
587
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
|
|
588
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
589
|
+
<polyline points="9 18 15 12 9 6"></polyline>
|
|
590
|
+
</svg>
|
|
591
|
+
</button>
|
|
592
|
+
</div>
|
|
593
|
+
`, styles: [":host{display:inline-block}.pagination-container{display:flex;align-items:center;gap:8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;-webkit-user-select:none;user-select:none;color:#606266}.nav-btn{background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:#909399;transition:color .2s,background-color .2s;border-radius:4px}.nav-btn:hover:not(:disabled){color:#303133;background-color:#f0f2f5}.nav-btn:disabled{cursor:not-allowed;color:#dcdfe6;opacity:.6}.page-info{font-size:14px;font-weight:500;letter-spacing:1px;color:#606266;display:flex;align-items:center;gap:4px}.separator{color:#909399}\n"] }]
|
|
594
|
+
}], propDecorators: { current: [{
|
|
595
|
+
type: Input
|
|
596
|
+
}], total: [{
|
|
597
|
+
type: Input
|
|
598
|
+
}], pageChange: [{
|
|
599
|
+
type: Output
|
|
600
|
+
}] } });
|
|
601
|
+
|
|
602
|
+
class MarkdownPipe {
|
|
603
|
+
constructor() {
|
|
604
|
+
this.md = inject(MarkdownStreamService);
|
|
605
|
+
}
|
|
606
|
+
transform(value) {
|
|
607
|
+
if (value !== null && value !== undefined) {
|
|
608
|
+
return this.md.renderIncremental(value);
|
|
609
|
+
}
|
|
610
|
+
return value;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
MarkdownPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
614
|
+
MarkdownPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: MarkdownPipe, isStandalone: true, name: "markdown" });
|
|
615
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownPipe, decorators: [{
|
|
616
|
+
type: Pipe,
|
|
617
|
+
args: [{
|
|
618
|
+
name: 'markdown',
|
|
619
|
+
standalone: true,
|
|
620
|
+
}]
|
|
621
|
+
}] });
|
|
622
|
+
|
|
623
|
+
// chat-messages.component.ts
|
|
624
|
+
class ChatMessagesComponent {
|
|
625
|
+
constructor() {
|
|
626
|
+
this.messages = [];
|
|
627
|
+
/**
|
|
628
|
+
* 重新生成回答
|
|
629
|
+
*/
|
|
630
|
+
this.regenerateAnswer = new EventEmitter();
|
|
631
|
+
this.md = inject(MarkdownStreamService);
|
|
632
|
+
this.sseService = inject(SseService);
|
|
633
|
+
this.sanitizer = inject(DomSanitizer);
|
|
634
|
+
this.msg = inject(NzMessageService);
|
|
635
|
+
this.isLoading = false;
|
|
636
|
+
this.inputHeight = 180;
|
|
637
|
+
this.renderedHtml = '';
|
|
638
|
+
this.accumulatedMarkdown = '';
|
|
639
|
+
}
|
|
640
|
+
ngAfterViewInit() {
|
|
641
|
+
this.sseService.loading$.subscribe(status => {
|
|
642
|
+
this.isLoading = status;
|
|
643
|
+
});
|
|
644
|
+
this.sseService.delta$.subscribe(delta => {
|
|
645
|
+
if (!delta)
|
|
646
|
+
return;
|
|
647
|
+
this.accumulatedMarkdown += delta;
|
|
648
|
+
this.renderedHtml = this.md.renderIncremental(this.accumulatedMarkdown);
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
ngAfterViewChecked() {
|
|
652
|
+
// 正在回答中
|
|
653
|
+
if (this.isLoading) {
|
|
654
|
+
this.scrollToBottom();
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
ngOnChanges(changes) {
|
|
658
|
+
if (changes['messages']) {
|
|
659
|
+
setTimeout(() => {
|
|
660
|
+
this.scrollToBottom();
|
|
661
|
+
}, 50);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
// 滚动到底部
|
|
665
|
+
scrollToBottom() {
|
|
666
|
+
if (!this.messagesContainer) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
this.messagesContainer.nativeElement.scrollTop = this.messagesContainer.nativeElement.scrollHeight;
|
|
670
|
+
}
|
|
671
|
+
copyContent(text) {
|
|
672
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
673
|
+
// 使用现代 Clipboard API
|
|
674
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
675
|
+
this.msg.success('复制成功');
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
onPageChange(num, msg) {
|
|
680
|
+
msg.answerIndex = num - 1;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* 重新生成多个回答
|
|
684
|
+
*/
|
|
685
|
+
regenerate(msg) {
|
|
686
|
+
// const question = this.messages[index - 1].message;
|
|
687
|
+
// 重新生成使用底部最新的配置,重新调用sendMessage
|
|
688
|
+
this.regenerateAnswer.emit(msg);
|
|
689
|
+
}
|
|
690
|
+
// 把完整的 markdown 块渲染,剩下的留给打字区
|
|
691
|
+
renderCompleteBlocks(markdown) {
|
|
692
|
+
// 找到最后一个换行符之前的都渲染(足够丝滑)
|
|
693
|
+
const lines = markdown.split('\n');
|
|
694
|
+
let breakPoint = lines.length;
|
|
695
|
+
// 从后往前找,保留未完成的代码块/表格/列表
|
|
696
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
697
|
+
const line = lines[i];
|
|
698
|
+
if (line.trim() === '' || line.startsWith('```') || line.startsWith('|') || line.startsWith('- ')) {
|
|
699
|
+
breakPoint = i;
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const completePart = lines.slice(0, breakPoint).join('\n');
|
|
704
|
+
const remainingPart = lines.slice(breakPoint).join('\n');
|
|
705
|
+
const html = this.md.renderIncremental(completePart);
|
|
706
|
+
// const html = marked(completePart, { breaks: true, gfm: true });
|
|
707
|
+
return { rendered: html, remaining: remainingPart };
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
ChatMessagesComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatMessagesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
711
|
+
ChatMessagesComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: ChatMessagesComponent, selector: "ngx-chat-messages", inputs: { messages: "messages" }, outputs: { regenerateAnswer: "regenerateAnswer" }, viewQueries: [{ propertyName: "messagesContainer", first: true, predicate: ["messagesContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- chat-messages.component.html -->\n<script src=\"chat-messages.component.ts\"></script>\n<div class=\"messages-container\" #messagesContainer>\n <div class=\"message-list\" [style.--input-height.px]=\"inputHeight\">\n <div class=\"question message-wrapper\" *ngFor=\"let msg of messages; let i = index\">\n <!-- Q -->\n <div class=\"bubble-wrapper user-msg\">\n <div class=\"message-bubble\">\n <div class=\"message-content message-human\">\n {{ msg.question }}\n </div>\n </div>\n </div>\n <!-- A -->\n <div class=\"bubble-wrapper ai-msg\" *ngIf=\"msg.answers[msg.answerIndex]\">\n <div\n class=\"bubble-loading\"\n *ngIf=\"!msg.answers[msg.answerIndex].think && !msg.answers[msg.answerIndex].content\"\n >\n <div class=\"bubble-loading-dot\" *ngFor=\"let item of [1, 2, 3]\"></div>\n </div>\n <div class=\"message-bubble\">\n <!-- \u601D\u8003 -->\n <div class=\"message-think\" *ngIf=\"msg.answers[msg.answerIndex].think\">\n <div\n class=\"message-think-header\"\n (click)=\"msg.answers[msg.answerIndex].thinkExpand = !msg.answers[msg.answerIndex].thinkExpand\"\n >\n <div class=\"message-think-header-title\">\n <img src=\"/assets/images/card/think-finish.png\" />\n {{ msg.answers[msg.answerIndex].content ? '\u5DF2\u5B8C\u6210\u601D\u8003' : '\u6B63\u5728\u601D\u8003\u4E2D...' }}\n </div>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <polyline points=\"6 9 12 15 18 9\"></polyline>\n </svg>\n </div>\n <div\n class=\"message-content message-assistant message-markdown\"\n [innerHTML]=\"msg.answers[msg.answerIndex].think | markdown\"\n [hidden]=\"!msg.answers[msg.answerIndex].thinkExpand\"\n ></div>\n </div>\n <!-- \u56DE\u7B54 -->\n <div\n class=\"message-content message-assistant message-markdown\"\n [innerHTML]=\"msg.answers[msg.answerIndex].content | markdown\"\n ></div>\n <!-- \u56DE\u7B54\u5DE5\u5177\u680F -->\n <div class=\"assistant-tools\" *ngIf=\"msg.answers[msg.answerIndex].content\">\n <ngx-simple-pagination\n [current]=\"msg.answerIndex + 1\"\n [total]=\"msg.answers.length\"\n (pageChange)=\"onPageChange($event, msg)\"\n *ngIf=\"msg.answers.length > 1\"\n ></ngx-simple-pagination>\n <img\n src=\"assets/images/card/copy.png\"\n alt=\"\u590D\u5236\"\n (click)=\"copyContent(msg.answers[msg.answerIndex].content)\"\n />\n <img\n src=\"assets/images/card/refresh.png\"\n alt=\"\u91CD\u65B0\u751F\u6210\"\n (click)=\"regenerate(msg)\"\n *ngIf=\"i === messages.length - 1\"\n />\n </div>\n </div>\n </div>\n </div>\n\n <!-- <div style=\"padding-bottom: 160px\"></div> -->\n </div>\n</div>\n", styles: [":host{width:100%;height:100%}.messages-container{width:100%;height:100%;overflow-y:auto}.message-list{height:auto;padding-top:16px;padding-bottom:var(--input-height, 210px);display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:16px}.message-wrapper{display:flex;flex-direction:column;justify-content:center;align-items:center;gap:16px;width:100%;margin-bottom:16px;max-width:800px;min-width:350px}.bubble-wrapper{width:100%;display:flex;flex-direction:column;justify-content:center}.bubble-wrapper.user-msg{align-items:end}.bubble-wrapper.ai-msg{align-items:start}.bubble-loading{display:flex;align-items:center;justify-content:flex-start;height:24px;margin-top:4px;margin-bottom:16px}.bubble-loading-dot{width:6px;height:6px;border-radius:50%;margin-right:6px;background-color:#bbb;animation:.9s linear infinite loading-dot}.bubble-loading-dot:first-child{animation-delay:0s}.bubble-loading-dot:nth-child(2){animation-delay:.1s}.bubble-loading-dot:nth-child(3){animation-delay:.2s}@keyframes loading-dot{0%,to{opacity:.3}33%,66%{opacity:1}}.message-bubble{width:100%;display:flex;align-items:flex-end;flex-direction:column;gap:8px}.message-bubble .message-content.message-human{padding:8px 16px;margin-left:auto;border-radius:12px;background:#E4EDFD;color:#004ad3}.message-bubble .message-content.message-assistant{padding:0;width:100%;background:#fff}.message-bubble .message-markdown{background:#fff}.message-bubble .message-think{width:100%;padding:12px;background:#FFFFFF;border-radius:10px;border:1px solid #D9DADB;font-size:14px}.message-bubble .message-think-header{display:flex;align-items:center;justify-content:space-between;cursor:pointer;color:#000;font-size:16px}.message-bubble .message-think-header-title{display:flex;align-items:center;gap:10px}.message-bubble .message-think-header-title img{width:14px;height:14px}.message-bubble .message-think .message-content{padding-top:12px;color:#61666b}.message-bubble .message-time{font-size:12px;color:#999}.assistant-tools{width:100%;display:flex;align-items:center;gap:24px}.assistant-tools img{width:14px;height:14px;cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SimplePaginationComponent, selector: "ngx-simple-pagination", inputs: ["current", "total"], outputs: ["pageChange"] }, { kind: "pipe", type: MarkdownPipe, name: "markdown" }] });
|
|
712
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatMessagesComponent, decorators: [{
|
|
713
|
+
type: Component,
|
|
714
|
+
args: [{ selector: 'ngx-chat-messages', template: "<!-- chat-messages.component.html -->\n<script src=\"chat-messages.component.ts\"></script>\n<div class=\"messages-container\" #messagesContainer>\n <div class=\"message-list\" [style.--input-height.px]=\"inputHeight\">\n <div class=\"question message-wrapper\" *ngFor=\"let msg of messages; let i = index\">\n <!-- Q -->\n <div class=\"bubble-wrapper user-msg\">\n <div class=\"message-bubble\">\n <div class=\"message-content message-human\">\n {{ msg.question }}\n </div>\n </div>\n </div>\n <!-- A -->\n <div class=\"bubble-wrapper ai-msg\" *ngIf=\"msg.answers[msg.answerIndex]\">\n <div\n class=\"bubble-loading\"\n *ngIf=\"!msg.answers[msg.answerIndex].think && !msg.answers[msg.answerIndex].content\"\n >\n <div class=\"bubble-loading-dot\" *ngFor=\"let item of [1, 2, 3]\"></div>\n </div>\n <div class=\"message-bubble\">\n <!-- \u601D\u8003 -->\n <div class=\"message-think\" *ngIf=\"msg.answers[msg.answerIndex].think\">\n <div\n class=\"message-think-header\"\n (click)=\"msg.answers[msg.answerIndex].thinkExpand = !msg.answers[msg.answerIndex].thinkExpand\"\n >\n <div class=\"message-think-header-title\">\n <img src=\"/assets/images/card/think-finish.png\" />\n {{ msg.answers[msg.answerIndex].content ? '\u5DF2\u5B8C\u6210\u601D\u8003' : '\u6B63\u5728\u601D\u8003\u4E2D...' }}\n </div>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <polyline points=\"6 9 12 15 18 9\"></polyline>\n </svg>\n </div>\n <div\n class=\"message-content message-assistant message-markdown\"\n [innerHTML]=\"msg.answers[msg.answerIndex].think | markdown\"\n [hidden]=\"!msg.answers[msg.answerIndex].thinkExpand\"\n ></div>\n </div>\n <!-- \u56DE\u7B54 -->\n <div\n class=\"message-content message-assistant message-markdown\"\n [innerHTML]=\"msg.answers[msg.answerIndex].content | markdown\"\n ></div>\n <!-- \u56DE\u7B54\u5DE5\u5177\u680F -->\n <div class=\"assistant-tools\" *ngIf=\"msg.answers[msg.answerIndex].content\">\n <ngx-simple-pagination\n [current]=\"msg.answerIndex + 1\"\n [total]=\"msg.answers.length\"\n (pageChange)=\"onPageChange($event, msg)\"\n *ngIf=\"msg.answers.length > 1\"\n ></ngx-simple-pagination>\n <img\n src=\"assets/images/card/copy.png\"\n alt=\"\u590D\u5236\"\n (click)=\"copyContent(msg.answers[msg.answerIndex].content)\"\n />\n <img\n src=\"assets/images/card/refresh.png\"\n alt=\"\u91CD\u65B0\u751F\u6210\"\n (click)=\"regenerate(msg)\"\n *ngIf=\"i === messages.length - 1\"\n />\n </div>\n </div>\n </div>\n </div>\n\n <!-- <div style=\"padding-bottom: 160px\"></div> -->\n </div>\n</div>\n", styles: [":host{width:100%;height:100%}.messages-container{width:100%;height:100%;overflow-y:auto}.message-list{height:auto;padding-top:16px;padding-bottom:var(--input-height, 210px);display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:16px}.message-wrapper{display:flex;flex-direction:column;justify-content:center;align-items:center;gap:16px;width:100%;margin-bottom:16px;max-width:800px;min-width:350px}.bubble-wrapper{width:100%;display:flex;flex-direction:column;justify-content:center}.bubble-wrapper.user-msg{align-items:end}.bubble-wrapper.ai-msg{align-items:start}.bubble-loading{display:flex;align-items:center;justify-content:flex-start;height:24px;margin-top:4px;margin-bottom:16px}.bubble-loading-dot{width:6px;height:6px;border-radius:50%;margin-right:6px;background-color:#bbb;animation:.9s linear infinite loading-dot}.bubble-loading-dot:first-child{animation-delay:0s}.bubble-loading-dot:nth-child(2){animation-delay:.1s}.bubble-loading-dot:nth-child(3){animation-delay:.2s}@keyframes loading-dot{0%,to{opacity:.3}33%,66%{opacity:1}}.message-bubble{width:100%;display:flex;align-items:flex-end;flex-direction:column;gap:8px}.message-bubble .message-content.message-human{padding:8px 16px;margin-left:auto;border-radius:12px;background:#E4EDFD;color:#004ad3}.message-bubble .message-content.message-assistant{padding:0;width:100%;background:#fff}.message-bubble .message-markdown{background:#fff}.message-bubble .message-think{width:100%;padding:12px;background:#FFFFFF;border-radius:10px;border:1px solid #D9DADB;font-size:14px}.message-bubble .message-think-header{display:flex;align-items:center;justify-content:space-between;cursor:pointer;color:#000;font-size:16px}.message-bubble .message-think-header-title{display:flex;align-items:center;gap:10px}.message-bubble .message-think-header-title img{width:14px;height:14px}.message-bubble .message-think .message-content{padding-top:12px;color:#61666b}.message-bubble .message-time{font-size:12px;color:#999}.assistant-tools{width:100%;display:flex;align-items:center;gap:24px}.assistant-tools img{width:14px;height:14px;cursor:pointer}\n"] }]
|
|
715
|
+
}], propDecorators: { messages: [{
|
|
716
|
+
type: Input
|
|
717
|
+
}], messagesContainer: [{
|
|
718
|
+
type: ViewChild,
|
|
719
|
+
args: ['messagesContainer']
|
|
720
|
+
}], regenerateAnswer: [{
|
|
721
|
+
type: Output
|
|
722
|
+
}] } });
|
|
723
|
+
|
|
724
|
+
class HttpService {
|
|
725
|
+
constructor() {
|
|
726
|
+
this.http = inject(HttpClient);
|
|
727
|
+
this.storeService = inject(StoreService);
|
|
728
|
+
this.BASE_URL = '/api'; // 或者完整域名
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* 统一获取 Headers
|
|
732
|
+
*/
|
|
733
|
+
getHeaders() {
|
|
734
|
+
return new HttpHeaders({
|
|
735
|
+
'Content-Type': 'application/json',
|
|
736
|
+
Authorization: `${this.storeService.getToken()}`,
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* GET 请求
|
|
741
|
+
*/
|
|
742
|
+
get(url, params) {
|
|
743
|
+
return this.http.get(this.formatUrl(url), {
|
|
744
|
+
headers: this.getHeaders(),
|
|
745
|
+
params: this.resolveParams(params),
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* POST 请求
|
|
750
|
+
*/
|
|
751
|
+
post(url, body) {
|
|
752
|
+
return this.http.post(this.formatUrl(url), body, {
|
|
753
|
+
headers: this.getHeaders(),
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* PUT 请求
|
|
758
|
+
*/
|
|
759
|
+
put(url, body) {
|
|
760
|
+
return this.http.put(this.formatUrl(url), body, {
|
|
761
|
+
headers: this.getHeaders(),
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* DELETE 请求
|
|
766
|
+
* 兼容支持:既支持 URL 参数,也支持 Body
|
|
767
|
+
*/
|
|
768
|
+
delete(url, params, body) {
|
|
769
|
+
return this.http.request('DELETE', this.formatUrl(url), {
|
|
770
|
+
headers: this.getHeaders(),
|
|
771
|
+
params: this.resolveParams(params),
|
|
772
|
+
body: body, // HttpClient 的 delete() 方法默认不支持 body,需用 request() 通用方法
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
// ================= 私有辅助方法 =================
|
|
776
|
+
/**
|
|
777
|
+
* 统一处理 URL
|
|
778
|
+
* 比如自动拼接 BaseUrl,或者处理斜杠
|
|
779
|
+
*/
|
|
780
|
+
formatUrl(url) {
|
|
781
|
+
if (url.startsWith('http'))
|
|
782
|
+
return url;
|
|
783
|
+
// 确保拼接顺滑
|
|
784
|
+
const baseUrl = this.storeService.getBaseUrl();
|
|
785
|
+
const cleanBase = baseUrl.replace(/\/$/, '');
|
|
786
|
+
const cleanUrl = url.startsWith('/') ? url : `/${url}`;
|
|
787
|
+
return `${cleanBase}${cleanUrl}`;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* 统一清洗参数
|
|
791
|
+
* 过滤掉 null 和 undefined,防止传给后端 "null" 字符串
|
|
792
|
+
*/
|
|
793
|
+
resolveParams(params) {
|
|
794
|
+
let httpParams = new HttpParams();
|
|
795
|
+
if (params) {
|
|
796
|
+
Object.keys(params).forEach(key => {
|
|
797
|
+
const value = params[key];
|
|
798
|
+
if (value !== null && value !== undefined) {
|
|
799
|
+
if (Array.isArray(value)) {
|
|
800
|
+
// 支持数组参数 ids: [1, 2] => ids=1&ids=2
|
|
801
|
+
value.forEach(val => (httpParams = httpParams.append(key, val)));
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
httpParams = httpParams.set(key, value);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
return httpParams;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
HttpService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
813
|
+
HttpService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService, providedIn: 'root' });
|
|
814
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService, decorators: [{
|
|
815
|
+
type: Injectable,
|
|
816
|
+
args: [{
|
|
817
|
+
providedIn: 'root',
|
|
818
|
+
}]
|
|
819
|
+
}] });
|
|
820
|
+
|
|
821
|
+
class HistoryGroupComponent {
|
|
822
|
+
constructor() {
|
|
823
|
+
this.select = new EventEmitter();
|
|
824
|
+
this.refresh = new EventEmitter();
|
|
825
|
+
this.currentSelected = null;
|
|
826
|
+
this.storeService = inject(StoreService);
|
|
827
|
+
this.modalService = inject(NzModalService);
|
|
828
|
+
this.msg = inject(NzMessageService);
|
|
829
|
+
this.http = inject(HttpService);
|
|
830
|
+
}
|
|
831
|
+
toggleExpand() {
|
|
832
|
+
this.group.expanded = !this.group.expanded;
|
|
833
|
+
}
|
|
834
|
+
selectConversation(item) {
|
|
835
|
+
if (item) {
|
|
836
|
+
item.isActive = true;
|
|
837
|
+
}
|
|
838
|
+
this.currentSelected = item;
|
|
839
|
+
this.select.emit(this.currentSelected);
|
|
840
|
+
this.storeService.setCurrentConversation(this.currentSelected);
|
|
841
|
+
}
|
|
842
|
+
deleteConversation(e, item) {
|
|
843
|
+
e.stopPropagation();
|
|
844
|
+
this.modalService.confirm({
|
|
845
|
+
nzTitle: '删除对话',
|
|
846
|
+
nzContent: `删除后无法恢复,是否确认删除“${item.name}”对话?`,
|
|
847
|
+
nzOkText: '确定',
|
|
848
|
+
nzCancelText: '取消',
|
|
849
|
+
nzOnOk: () => {
|
|
850
|
+
this.http.delete(`/api/ChatModel/conversation`, { iCode: item.iCode }).subscribe(res => {
|
|
851
|
+
this.msg.success('删除成功');
|
|
852
|
+
this.refresh.emit(item.iCode);
|
|
853
|
+
});
|
|
854
|
+
},
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
HistoryGroupComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HistoryGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
859
|
+
HistoryGroupComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: HistoryGroupComponent, selector: "history-group", inputs: { group: "group" }, outputs: { select: "select", refresh: "refresh" }, ngImport: i0, template: `
|
|
860
|
+
<div class="group-container">
|
|
861
|
+
<div class="group-header" (click)="toggleExpand()">
|
|
862
|
+
<div class="left">
|
|
863
|
+
<img src="assets/images/sidebar/time.png" />
|
|
864
|
+
<span class="date-text">{{ group.dateLabel }}</span>
|
|
865
|
+
</div>
|
|
866
|
+
<div class="arrow-icon" [class.rotated]="group.expanded">
|
|
867
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
868
|
+
<polyline points="6 9 12 15 18 9"></polyline>
|
|
869
|
+
</svg>
|
|
870
|
+
</div>
|
|
871
|
+
</div>
|
|
872
|
+
|
|
873
|
+
<div class="group-content" [@expandCollapse]="group.expanded ? 'expanded' : 'collapsed'">
|
|
874
|
+
<div
|
|
875
|
+
*ngFor="let item of group.items"
|
|
876
|
+
class="history-item"
|
|
877
|
+
[class.active]="item.isActive"
|
|
878
|
+
(click)="selectConversation(item)"
|
|
879
|
+
>
|
|
880
|
+
<span class="item-title">{{ item.name }}</span>
|
|
881
|
+
|
|
882
|
+
<img class="delete-img" src="assets/images/sidebar/delete.png" (click)="deleteConversation($event, item)" />
|
|
883
|
+
</div>
|
|
884
|
+
</div>
|
|
885
|
+
</div>
|
|
886
|
+
`, isInline: true, styles: ["@charset \"UTF-8\";.group-header{display:flex;justify-content:space-between;align-items:center;padding:12px 0;cursor:pointer;color:#999;font-size:14px;-webkit-user-select:none;user-select:none}.group-header:hover{color:#666}.left{display:flex;align-items:center;gap:8px}.left img{width:14px;height:14px}.arrow-icon{transition:transform .3s ease}.arrow-icon.rotated{transform:rotate(0)}.arrow-icon:not(.rotated){transform:rotate(-90deg)}.group-content{overflow:hidden}.history-item{padding:10px 12px;margin-bottom:4px;border-radius:8px;font-size:14px;color:#444;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:background-color .2s}.history-item .delete-img{width:14px;height:14px;display:none}.history-item:hover{background-color:#ebeced}.history-item:hover:hover .delete-img{display:block}.history-item.active{background-color:#e4edfd;color:#004ad3}.item-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], animations: [
|
|
887
|
+
trigger('expandCollapse', [
|
|
888
|
+
state('collapsed', style({ height: '0px', opacity: 0 })),
|
|
889
|
+
state('expanded', style({ height: '*', opacity: 1 })),
|
|
890
|
+
transition('expanded <=> collapsed', animate('300ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
|
|
891
|
+
]),
|
|
892
|
+
] });
|
|
893
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HistoryGroupComponent, decorators: [{
|
|
894
|
+
type: Component,
|
|
895
|
+
args: [{ selector: 'history-group', template: `
|
|
896
|
+
<div class="group-container">
|
|
897
|
+
<div class="group-header" (click)="toggleExpand()">
|
|
898
|
+
<div class="left">
|
|
899
|
+
<img src="assets/images/sidebar/time.png" />
|
|
900
|
+
<span class="date-text">{{ group.dateLabel }}</span>
|
|
901
|
+
</div>
|
|
902
|
+
<div class="arrow-icon" [class.rotated]="group.expanded">
|
|
903
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
904
|
+
<polyline points="6 9 12 15 18 9"></polyline>
|
|
905
|
+
</svg>
|
|
906
|
+
</div>
|
|
907
|
+
</div>
|
|
908
|
+
|
|
909
|
+
<div class="group-content" [@expandCollapse]="group.expanded ? 'expanded' : 'collapsed'">
|
|
910
|
+
<div
|
|
911
|
+
*ngFor="let item of group.items"
|
|
912
|
+
class="history-item"
|
|
913
|
+
[class.active]="item.isActive"
|
|
914
|
+
(click)="selectConversation(item)"
|
|
915
|
+
>
|
|
916
|
+
<span class="item-title">{{ item.name }}</span>
|
|
917
|
+
|
|
918
|
+
<img class="delete-img" src="assets/images/sidebar/delete.png" (click)="deleteConversation($event, item)" />
|
|
919
|
+
</div>
|
|
920
|
+
</div>
|
|
921
|
+
</div>
|
|
922
|
+
`, animations: [
|
|
923
|
+
trigger('expandCollapse', [
|
|
924
|
+
state('collapsed', style({ height: '0px', opacity: 0 })),
|
|
925
|
+
state('expanded', style({ height: '*', opacity: 1 })),
|
|
926
|
+
transition('expanded <=> collapsed', animate('300ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
|
|
927
|
+
]),
|
|
928
|
+
], styles: ["@charset \"UTF-8\";.group-header{display:flex;justify-content:space-between;align-items:center;padding:12px 0;cursor:pointer;color:#999;font-size:14px;-webkit-user-select:none;user-select:none}.group-header:hover{color:#666}.left{display:flex;align-items:center;gap:8px}.left img{width:14px;height:14px}.arrow-icon{transition:transform .3s ease}.arrow-icon.rotated{transform:rotate(0)}.arrow-icon:not(.rotated){transform:rotate(-90deg)}.group-content{overflow:hidden}.history-item{padding:10px 12px;margin-bottom:4px;border-radius:8px;font-size:14px;color:#444;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:background-color .2s}.history-item .delete-img{width:14px;height:14px;display:none}.history-item:hover{background-color:#ebeced}.history-item:hover:hover .delete-img{display:block}.history-item.active{background-color:#e4edfd;color:#004ad3}.item-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}\n"] }]
|
|
929
|
+
}], propDecorators: { group: [{
|
|
930
|
+
type: Input
|
|
931
|
+
}], select: [{
|
|
932
|
+
type: Output
|
|
933
|
+
}], refresh: [{
|
|
934
|
+
type: Output
|
|
935
|
+
}] } });
|
|
936
|
+
|
|
937
|
+
// chat-sidebar.component.ts
|
|
938
|
+
class ChatSidebarComponent {
|
|
939
|
+
constructor() {
|
|
940
|
+
this.http = inject(HttpService$1);
|
|
941
|
+
this.storeService = inject(StoreService);
|
|
942
|
+
this.isCollapsed = false;
|
|
943
|
+
this.historyGroups = [];
|
|
944
|
+
}
|
|
945
|
+
ngOnInit() {
|
|
946
|
+
this.getHistory();
|
|
947
|
+
}
|
|
948
|
+
// 获取历史对话
|
|
949
|
+
getHistory(needSelectFirst = false) {
|
|
950
|
+
this.http.get(`${this.storeService.getBaseUrl()}/api/ChatModel/conversation`).subscribe((res) => {
|
|
951
|
+
var _a;
|
|
952
|
+
this.historyGroups = this.groupByFriendlyTime(res);
|
|
953
|
+
// 选中第一个对话
|
|
954
|
+
if (needSelectFirst) {
|
|
955
|
+
if ((_a = this.historyGroups[0]) === null || _a === void 0 ? void 0 : _a.items[0]) {
|
|
956
|
+
this.historyGroupRef.selectConversation(this.historyGroups[0].items[0]);
|
|
957
|
+
}
|
|
958
|
+
else {
|
|
959
|
+
this.historyGroupRef.selectConversation(null);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
// 删除后刷新,若删除当前选中会话
|
|
965
|
+
refresh(conversationICode) {
|
|
966
|
+
const needSelectFirst = this.storeService.getConversationICode() === conversationICode;
|
|
967
|
+
this.getHistory(needSelectFirst);
|
|
968
|
+
}
|
|
969
|
+
selectConversation(conversation) {
|
|
970
|
+
// 取消勾选其他会话
|
|
971
|
+
this.historyGroups.forEach(group => {
|
|
972
|
+
group.items.forEach(item => {
|
|
973
|
+
if (!conversation || conversation.iCode !== item.iCode) {
|
|
974
|
+
item.isActive = false;
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
toggleSidebar() {
|
|
980
|
+
this.isCollapsed = !this.isCollapsed;
|
|
981
|
+
}
|
|
982
|
+
openNewChat() {
|
|
983
|
+
this.historyGroupRef.selectConversation(null);
|
|
984
|
+
}
|
|
985
|
+
groupByFriendlyTime(dataList) {
|
|
986
|
+
if (!dataList || dataList.length === 0) {
|
|
987
|
+
return [];
|
|
988
|
+
}
|
|
989
|
+
const list = [];
|
|
990
|
+
const now = new Date();
|
|
991
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
992
|
+
const yesterday = new Date(today);
|
|
993
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
994
|
+
dataList.forEach((item, index) => {
|
|
995
|
+
const itemTime = new Date(item.time);
|
|
996
|
+
const itemDate = new Date(itemTime.getFullYear(), itemTime.getMonth(), itemTime.getDate());
|
|
997
|
+
let dateLabel = '';
|
|
998
|
+
item.isActive = false;
|
|
999
|
+
if (itemDate.getTime() === today.getTime()) {
|
|
1000
|
+
dateLabel = '今天';
|
|
1001
|
+
}
|
|
1002
|
+
else if (itemDate.getTime() === yesterday.getTime()) {
|
|
1003
|
+
dateLabel = '昨天';
|
|
1004
|
+
}
|
|
1005
|
+
else {
|
|
1006
|
+
// 其他日期显示具体的年月日
|
|
1007
|
+
dateLabel = `${itemTime.getFullYear()}年${(itemTime.getMonth() + 1).toString().padStart(2, '0')}月${itemTime.getDate().toString().padStart(2, '0')}日`;
|
|
1008
|
+
}
|
|
1009
|
+
const find = list.find(item => item.dateLabel === dateLabel);
|
|
1010
|
+
if (find) {
|
|
1011
|
+
find.items.push(item);
|
|
1012
|
+
}
|
|
1013
|
+
else {
|
|
1014
|
+
const group = { dateLabel: '', expanded: index === 0, items: [] };
|
|
1015
|
+
group.dateLabel = dateLabel;
|
|
1016
|
+
group.items.push(item);
|
|
1017
|
+
list.push(group);
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
return list;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
ChatSidebarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1024
|
+
ChatSidebarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: ChatSidebarComponent, selector: "ngx-chat-sidebar", viewQueries: [{ propertyName: "historyGroupRef", first: true, predicate: ["historyGroupRef"], descendants: true }], ngImport: i0, template: "<div class=\"sidebar\" [class.collapsed]=\"isCollapsed\">\n <div class=\"sidebar-inner\">\n <div class=\"sidebar-header\">\n <div class=\"logo-area\">\n <img src=\"assets/images/logo.png\" alt=\"Logo\" class=\"logo-img\" />\n <span class=\"app-name\">\u9752\u6E05\u6C34\u5229</span>\n </div>\n <button class=\"toggle-btn\" (click)=\"toggleSidebar()\">\n <img src=\"assets/images/sidebar/collapse.png\" />\n </button>\n </div>\n\n <div class=\"sidebar-content\">\n <button class=\"new-chat-btn\" (click)=\"openNewChat()\">\n <img src=\"assets/images/sidebar/new-chat.png\" />\n \u5F00\u542F\u65B0\u5BF9\u8BDD\n </button>\n\n <div class=\"divider\"></div>\n\n <div class=\"section-title\">\n <img src=\"assets/images/sidebar/history-chat.png\" />\n <span>\u5386\u53F2\u5BF9\u8BDD</span>\n </div>\n\n <div class=\"history-list-scroll\" *ngIf=\"historyGroups.length\">\n <history-group\n #historyGroupRef\n *ngFor=\"let group of historyGroups\"\n [group]=\"group\"\n (select)=\"selectConversation($event)\"\n (refresh)=\"refresh($event)\"\n >\n </history-group>\n </div>\n\n <div class=\"history-empty\" *ngIf=\"!historyGroups.length\">\u6682\u65E0\u5386\u53F2\u5BF9\u8BDD</div>\n </div>\n </div>\n</div>\n\n<div class=\"logo-actions\" [@logoActionsAnimation]=\"isCollapsed ? 'leave' : 'enter'\" *ngIf=\"isCollapsed\">\n <img src=\"assets/images/logo.png\" alt=\"Logo\" class=\"logo-img\" />\n <div class=\"left-top-bar\">\n <img src=\"assets/images/sidebar/expand.png\" (click)=\"toggleSidebar()\" />\n <img src=\"assets/images/sidebar/new-chat.png\" (click)=\"openNewChat()\" />\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100vh}.sidebar{width:280px;height:100%;background-color:#f3f4f6;border-right:1px solid #e0e0e0;display:flex;flex-direction:column;transition:width .4s cubic-bezier(.25,.8,.25,1),border .4s;overflow:hidden;box-sizing:border-box}.sidebar.collapsed{width:0;padding:0;border-right:0 solid transparent}.sidebar-inner{width:280px;height:100%;display:flex;flex-direction:column;padding:16px;box-sizing:border-box}.sidebar-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;height:32px}.logo-area{display:flex;align-items:center;gap:8px;white-space:nowrap}.logo-img{width:28px;height:28px;object-fit:contain}.app-name{font-weight:600;font-size:16px;color:#000}.toggle-btn{background:none;border:none;cursor:pointer;color:#666;padding:4px;border-radius:4px}.toggle-btn img{width:16px;height:15px}.toggle-btn:hover{background-color:#0000000d}.sidebar-content{flex:1;display:flex;flex-direction:column;overflow:hidden}.new-chat-btn{width:100%;background-color:#fff;border:1px solid #e0e0e0;border-radius:20px;padding:10px;color:#333;font-weight:500;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;box-shadow:0 2px 5px #00000008;transition:all .2s;white-space:nowrap}.new-chat-btn img{width:16px;height:16px}.new-chat-btn:hover{box-shadow:0 4px 8px #00000014;border-color:#d0d0d0}.divider{height:1px;background-color:#e0e0e0;margin:20px 0}.section-title{display:flex;align-items:center;gap:8px;font-weight:600;color:#333;margin-bottom:10px;white-space:nowrap}.section-title img{width:16px;height:16px}.history-list-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding-right:4px}.history-empty{flex:1;display:flex;align-items:center;justify-content:center}.logo-actions{position:absolute;left:12px;top:12px;display:flex;align-items:center;gap:8px;z-index:99}.logo-actions .logo-img{width:40px;height:32px}.logo-actions .left-top-bar{width:75px;height:40px;display:flex;align-items:center;justify-content:center;gap:19px;background:#FFFFFF;box-shadow:0 2px 4px #0000001a;border-radius:20px;border:1px solid #EEEEEE}.logo-actions .left-top-bar img{width:16px;height:16px;cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: HistoryGroupComponent, selector: "history-group", inputs: ["group"], outputs: ["select", "refresh"] }], animations: [
|
|
1025
|
+
trigger('logoActionsAnimation', [
|
|
1026
|
+
transition(':enter', [
|
|
1027
|
+
style({ opacity: 0, transform: 'translateX(20px)' }),
|
|
1028
|
+
animate('600ms ease-out', style({ opacity: 1, transform: 'translateX(0)' })),
|
|
1029
|
+
]),
|
|
1030
|
+
transition(':leave', [animate('0ms ease-in', style({ opacity: 0, transform: 'translateX(20px)' }))]),
|
|
1031
|
+
]),
|
|
1032
|
+
] });
|
|
1033
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatSidebarComponent, decorators: [{
|
|
1034
|
+
type: Component,
|
|
1035
|
+
args: [{ selector: 'ngx-chat-sidebar', animations: [
|
|
1036
|
+
trigger('logoActionsAnimation', [
|
|
1037
|
+
transition(':enter', [
|
|
1038
|
+
style({ opacity: 0, transform: 'translateX(20px)' }),
|
|
1039
|
+
animate('600ms ease-out', style({ opacity: 1, transform: 'translateX(0)' })),
|
|
1040
|
+
]),
|
|
1041
|
+
transition(':leave', [animate('0ms ease-in', style({ opacity: 0, transform: 'translateX(20px)' }))]),
|
|
1042
|
+
]),
|
|
1043
|
+
], template: "<div class=\"sidebar\" [class.collapsed]=\"isCollapsed\">\n <div class=\"sidebar-inner\">\n <div class=\"sidebar-header\">\n <div class=\"logo-area\">\n <img src=\"assets/images/logo.png\" alt=\"Logo\" class=\"logo-img\" />\n <span class=\"app-name\">\u9752\u6E05\u6C34\u5229</span>\n </div>\n <button class=\"toggle-btn\" (click)=\"toggleSidebar()\">\n <img src=\"assets/images/sidebar/collapse.png\" />\n </button>\n </div>\n\n <div class=\"sidebar-content\">\n <button class=\"new-chat-btn\" (click)=\"openNewChat()\">\n <img src=\"assets/images/sidebar/new-chat.png\" />\n \u5F00\u542F\u65B0\u5BF9\u8BDD\n </button>\n\n <div class=\"divider\"></div>\n\n <div class=\"section-title\">\n <img src=\"assets/images/sidebar/history-chat.png\" />\n <span>\u5386\u53F2\u5BF9\u8BDD</span>\n </div>\n\n <div class=\"history-list-scroll\" *ngIf=\"historyGroups.length\">\n <history-group\n #historyGroupRef\n *ngFor=\"let group of historyGroups\"\n [group]=\"group\"\n (select)=\"selectConversation($event)\"\n (refresh)=\"refresh($event)\"\n >\n </history-group>\n </div>\n\n <div class=\"history-empty\" *ngIf=\"!historyGroups.length\">\u6682\u65E0\u5386\u53F2\u5BF9\u8BDD</div>\n </div>\n </div>\n</div>\n\n<div class=\"logo-actions\" [@logoActionsAnimation]=\"isCollapsed ? 'leave' : 'enter'\" *ngIf=\"isCollapsed\">\n <img src=\"assets/images/logo.png\" alt=\"Logo\" class=\"logo-img\" />\n <div class=\"left-top-bar\">\n <img src=\"assets/images/sidebar/expand.png\" (click)=\"toggleSidebar()\" />\n <img src=\"assets/images/sidebar/new-chat.png\" (click)=\"openNewChat()\" />\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100vh}.sidebar{width:280px;height:100%;background-color:#f3f4f6;border-right:1px solid #e0e0e0;display:flex;flex-direction:column;transition:width .4s cubic-bezier(.25,.8,.25,1),border .4s;overflow:hidden;box-sizing:border-box}.sidebar.collapsed{width:0;padding:0;border-right:0 solid transparent}.sidebar-inner{width:280px;height:100%;display:flex;flex-direction:column;padding:16px;box-sizing:border-box}.sidebar-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;height:32px}.logo-area{display:flex;align-items:center;gap:8px;white-space:nowrap}.logo-img{width:28px;height:28px;object-fit:contain}.app-name{font-weight:600;font-size:16px;color:#000}.toggle-btn{background:none;border:none;cursor:pointer;color:#666;padding:4px;border-radius:4px}.toggle-btn img{width:16px;height:15px}.toggle-btn:hover{background-color:#0000000d}.sidebar-content{flex:1;display:flex;flex-direction:column;overflow:hidden}.new-chat-btn{width:100%;background-color:#fff;border:1px solid #e0e0e0;border-radius:20px;padding:10px;color:#333;font-weight:500;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;box-shadow:0 2px 5px #00000008;transition:all .2s;white-space:nowrap}.new-chat-btn img{width:16px;height:16px}.new-chat-btn:hover{box-shadow:0 4px 8px #00000014;border-color:#d0d0d0}.divider{height:1px;background-color:#e0e0e0;margin:20px 0}.section-title{display:flex;align-items:center;gap:8px;font-weight:600;color:#333;margin-bottom:10px;white-space:nowrap}.section-title img{width:16px;height:16px}.history-list-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding-right:4px}.history-empty{flex:1;display:flex;align-items:center;justify-content:center}.logo-actions{position:absolute;left:12px;top:12px;display:flex;align-items:center;gap:8px;z-index:99}.logo-actions .logo-img{width:40px;height:32px}.logo-actions .left-top-bar{width:75px;height:40px;display:flex;align-items:center;justify-content:center;gap:19px;background:#FFFFFF;box-shadow:0 2px 4px #0000001a;border-radius:20px;border:1px solid #EEEEEE}.logo-actions .left-top-bar img{width:16px;height:16px;cursor:pointer}\n"] }]
|
|
1044
|
+
}], propDecorators: { historyGroupRef: [{
|
|
1045
|
+
type: ViewChild,
|
|
1046
|
+
args: ['historyGroupRef']
|
|
1047
|
+
}] } });
|
|
1048
|
+
|
|
1049
|
+
class NgxAgentChatComponent {
|
|
1050
|
+
constructor() {
|
|
1051
|
+
/**
|
|
1052
|
+
* 接口baseUrl配置
|
|
1053
|
+
*/
|
|
1054
|
+
this.baseUrl = 'http://39.102.56.254:8082';
|
|
1055
|
+
this.token = '';
|
|
1056
|
+
this.md = inject(MarkdownStreamService);
|
|
1057
|
+
this.httpService = inject(HttpService$1);
|
|
1058
|
+
this.sseService = inject(SseService);
|
|
1059
|
+
this.storeService = inject(StoreService);
|
|
1060
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1061
|
+
this.ngZone = inject(NgZone);
|
|
1062
|
+
this.messages = []; // 消息数组
|
|
1063
|
+
}
|
|
1064
|
+
ngOnInit() {
|
|
1065
|
+
this.storeService.setToken(this.token);
|
|
1066
|
+
this.storeService.setBaseUrl(this.baseUrl);
|
|
1067
|
+
this.sseService.conversationICode$.subscribe(iCodeObj => {
|
|
1068
|
+
if (iCodeObj) {
|
|
1069
|
+
const currentMsg = this.messages[this.messages.length - 1];
|
|
1070
|
+
currentMsg.iCode = iCodeObj.question;
|
|
1071
|
+
currentMsg.answers[currentMsg.answerIndex].iCode = iCodeObj.answer;
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
this.sseService.thinking$.subscribe(text => {
|
|
1075
|
+
if (this.messages.length > 0) {
|
|
1076
|
+
const currentMsg = this.messages[this.messages.length - 1];
|
|
1077
|
+
currentMsg.answers[currentMsg.answerIndex].think = text;
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
this.sseService.currentMessage$.subscribe(text => {
|
|
1081
|
+
if (this.messages.length > 0) {
|
|
1082
|
+
const currentMsg = this.messages[this.messages.length - 1];
|
|
1083
|
+
currentMsg.answers[currentMsg.answerIndex].content = text;
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
this.storeService.currentConversation$.subscribe(conversation => {
|
|
1087
|
+
if (!conversation) {
|
|
1088
|
+
this.messages = [];
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
this.httpService
|
|
1092
|
+
.get(`${this.storeService.getBaseUrl()}/api/ChatModel/conversation/questions`, { iCode: conversation === null || conversation === void 0 ? void 0 : conversation.iCode })
|
|
1093
|
+
.subscribe((res) => {
|
|
1094
|
+
this.messages = res.map(item => {
|
|
1095
|
+
item.answerIndex = 0;
|
|
1096
|
+
item.answers.forEach(answer => {
|
|
1097
|
+
answer.thinkExpand = true;
|
|
1098
|
+
});
|
|
1099
|
+
return item;
|
|
1100
|
+
});
|
|
1101
|
+
});
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
ngAfterViewInit() {
|
|
1105
|
+
this.resizeObserver = new ResizeObserver(entries => {
|
|
1106
|
+
for (let entry of entries) {
|
|
1107
|
+
const newHeight = entry.contentRect.height;
|
|
1108
|
+
this.ngZone.run(() => {
|
|
1109
|
+
this.chatMessagesRef.inputHeight = newHeight + 60;
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
// 开始监听输入框元素
|
|
1114
|
+
this.resizeObserver.observe(this.chatInputRef.chatInputWrapper.nativeElement);
|
|
1115
|
+
}
|
|
1116
|
+
// 重新生成回答(仅最新的回答允许重新生成)
|
|
1117
|
+
regenerateAnswer(msg) {
|
|
1118
|
+
this.chatInputRef.regenerateSend(msg);
|
|
1119
|
+
}
|
|
1120
|
+
// 发起提问请求
|
|
1121
|
+
onSendMessage(event) {
|
|
1122
|
+
if (!event.isRegenerate) {
|
|
1123
|
+
// 推入一个新问答,iCode在流接收到补上
|
|
1124
|
+
const qa = {
|
|
1125
|
+
iCode: '',
|
|
1126
|
+
question: event.message.question,
|
|
1127
|
+
answers: [
|
|
1128
|
+
{
|
|
1129
|
+
iCode: '',
|
|
1130
|
+
think: '',
|
|
1131
|
+
content: '',
|
|
1132
|
+
thinkExpand: true,
|
|
1133
|
+
},
|
|
1134
|
+
],
|
|
1135
|
+
answerIndex: 0,
|
|
1136
|
+
};
|
|
1137
|
+
this.messages.push(qa);
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1140
|
+
const latestMessage = this.messages[this.messages.length - 1];
|
|
1141
|
+
latestMessage.answers.push({
|
|
1142
|
+
iCode: '',
|
|
1143
|
+
think: '',
|
|
1144
|
+
content: '',
|
|
1145
|
+
thinkExpand: true,
|
|
1146
|
+
});
|
|
1147
|
+
latestMessage.answerIndex = latestMessage.answers.length - 1;
|
|
1148
|
+
}
|
|
1149
|
+
this.sseService.streamChat(event.message).then(res => {
|
|
1150
|
+
// 开启新对话,刷新左侧历史对话
|
|
1151
|
+
if (!event.message.conversation) {
|
|
1152
|
+
this.chatSidebarRef.getHistory(true);
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
NgxAgentChatComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NgxAgentChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1158
|
+
NgxAgentChatComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: NgxAgentChatComponent, selector: "ngx-agent-chat", inputs: { baseUrl: "baseUrl", token: "token" }, viewQueries: [{ propertyName: "chatSidebarRef", first: true, predicate: ["chatSidebarRef"], descendants: true }, { propertyName: "chatMessagesRef", first: true, predicate: ["chatMessagesRef"], descendants: true }, { propertyName: "chatInputRef", first: true, predicate: ["chatInputRef"], descendants: true }], ngImport: i0, template: "<div class=\"qq-agent\">\n <!-- \u5DE6\u4FA7\u8FB9\u680F -->\n <ngx-chat-sidebar #chatSidebarRef></ngx-chat-sidebar>\n\n <!-- \u53F3\u4FA7\u4E3B\u5185\u5BB9 -->\n <div class=\"agent-main\">\n <ngx-chat-messages #chatMessagesRef [messages]=\"messages\" (regenerateAnswer)=\"regenerateAnswer($event)\"></ngx-chat-messages>\n <ngx-chat-input #chatInputRef [messages]=\"messages\" (sendMessage)=\"onSendMessage($event)\"></ngx-chat-input>\n </div>\n</div>", styles: [":host{width:100%;height:100%}:host ::ng-deep ::-webkit-scrollbar{width:6px}:host ::ng-deep ::-webkit-scrollbar-track{background:transparent}:host ::ng-deep ::-webkit-scrollbar-thumb{background:#d1d1d1;border-radius:8px}:host ::ng-deep ::-webkit-scrollbar-thumb:hover{background:#b0b0b0}.qq-agent{width:100%;height:100%;display:flex;overflow:hidden}.qq-agent .agent-left{width:280px;height:100%;background:#F3F4F6}.qq-agent .agent-main{position:relative;width:100%;height:100%;display:flex;flex-direction:column;justify-content:space-between;align-items:center}\n"], dependencies: [{ kind: "component", type: ChatInputComponent, selector: "ngx-chat-input", inputs: ["messages"], outputs: ["sendMessage", "messageChange", "enter", "paste"] }, { kind: "component", type: ChatMessagesComponent, selector: "ngx-chat-messages", inputs: ["messages"], outputs: ["regenerateAnswer"] }, { kind: "component", type: ChatSidebarComponent, selector: "ngx-chat-sidebar" }] });
|
|
1159
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NgxAgentChatComponent, decorators: [{
|
|
1160
|
+
type: Component,
|
|
1161
|
+
args: [{ selector: 'ngx-agent-chat', template: "<div class=\"qq-agent\">\n <!-- \u5DE6\u4FA7\u8FB9\u680F -->\n <ngx-chat-sidebar #chatSidebarRef></ngx-chat-sidebar>\n\n <!-- \u53F3\u4FA7\u4E3B\u5185\u5BB9 -->\n <div class=\"agent-main\">\n <ngx-chat-messages #chatMessagesRef [messages]=\"messages\" (regenerateAnswer)=\"regenerateAnswer($event)\"></ngx-chat-messages>\n <ngx-chat-input #chatInputRef [messages]=\"messages\" (sendMessage)=\"onSendMessage($event)\"></ngx-chat-input>\n </div>\n</div>", styles: [":host{width:100%;height:100%}:host ::ng-deep ::-webkit-scrollbar{width:6px}:host ::ng-deep ::-webkit-scrollbar-track{background:transparent}:host ::ng-deep ::-webkit-scrollbar-thumb{background:#d1d1d1;border-radius:8px}:host ::ng-deep ::-webkit-scrollbar-thumb:hover{background:#b0b0b0}.qq-agent{width:100%;height:100%;display:flex;overflow:hidden}.qq-agent .agent-left{width:280px;height:100%;background:#F3F4F6}.qq-agent .agent-main{position:relative;width:100%;height:100%;display:flex;flex-direction:column;justify-content:space-between;align-items:center}\n"] }]
|
|
1162
|
+
}], propDecorators: { baseUrl: [{
|
|
1163
|
+
type: Input
|
|
1164
|
+
}], token: [{
|
|
1165
|
+
type: Input
|
|
1166
|
+
}], chatSidebarRef: [{
|
|
1167
|
+
type: ViewChild,
|
|
1168
|
+
args: ['chatSidebarRef']
|
|
1169
|
+
}], chatMessagesRef: [{
|
|
1170
|
+
type: ViewChild,
|
|
1171
|
+
args: ['chatMessagesRef']
|
|
1172
|
+
}], chatInputRef: [{
|
|
1173
|
+
type: ViewChild,
|
|
1174
|
+
args: ['chatInputRef']
|
|
1175
|
+
}] } });
|
|
1176
|
+
|
|
1177
|
+
class AgentChatModule {
|
|
1178
|
+
}
|
|
1179
|
+
AgentChatModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1180
|
+
AgentChatModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, declarations: [NgxAgentChatComponent,
|
|
1181
|
+
ChatInputComponent,
|
|
1182
|
+
ChatMessagesComponent,
|
|
1183
|
+
ChatSidebarComponent,
|
|
1184
|
+
SimplePaginationComponent,
|
|
1185
|
+
HistoryGroupComponent], imports: [CommonModule,
|
|
1186
|
+
NzAvatarModule,
|
|
1187
|
+
NzSpinModule,
|
|
1188
|
+
NzTagModule,
|
|
1189
|
+
NzInputModule,
|
|
1190
|
+
NzButtonModule,
|
|
1191
|
+
NzListModule,
|
|
1192
|
+
NzSliderModule,
|
|
1193
|
+
NzLayoutModule,
|
|
1194
|
+
NzPopoverModule,
|
|
1195
|
+
NzRadioModule,
|
|
1196
|
+
NzCheckboxModule,
|
|
1197
|
+
NzIconModule,
|
|
1198
|
+
NzPaginationModule,
|
|
1199
|
+
FormsModule,
|
|
1200
|
+
MarkdownPipe], exports: [NgxAgentChatComponent] });
|
|
1201
|
+
AgentChatModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, providers: [NzMessageService], imports: [CommonModule,
|
|
1202
|
+
NzAvatarModule,
|
|
1203
|
+
NzSpinModule,
|
|
1204
|
+
NzTagModule,
|
|
1205
|
+
NzInputModule,
|
|
1206
|
+
NzButtonModule,
|
|
1207
|
+
NzListModule,
|
|
1208
|
+
NzSliderModule,
|
|
1209
|
+
NzLayoutModule,
|
|
1210
|
+
NzPopoverModule,
|
|
1211
|
+
NzRadioModule,
|
|
1212
|
+
NzCheckboxModule,
|
|
1213
|
+
NzIconModule,
|
|
1214
|
+
NzPaginationModule,
|
|
1215
|
+
FormsModule] });
|
|
1216
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, decorators: [{
|
|
1217
|
+
type: NgModule,
|
|
1218
|
+
args: [{
|
|
1219
|
+
declarations: [
|
|
1220
|
+
NgxAgentChatComponent,
|
|
1221
|
+
ChatInputComponent,
|
|
1222
|
+
ChatMessagesComponent,
|
|
1223
|
+
ChatSidebarComponent,
|
|
1224
|
+
SimplePaginationComponent,
|
|
1225
|
+
HistoryGroupComponent,
|
|
1226
|
+
],
|
|
1227
|
+
imports: [
|
|
1228
|
+
CommonModule,
|
|
1229
|
+
NzAvatarModule,
|
|
1230
|
+
NzSpinModule,
|
|
1231
|
+
NzTagModule,
|
|
1232
|
+
NzInputModule,
|
|
1233
|
+
NzButtonModule,
|
|
1234
|
+
NzListModule,
|
|
1235
|
+
NzSliderModule,
|
|
1236
|
+
NzLayoutModule,
|
|
1237
|
+
NzPopoverModule,
|
|
1238
|
+
NzRadioModule,
|
|
1239
|
+
NzCheckboxModule,
|
|
1240
|
+
NzIconModule,
|
|
1241
|
+
NzPaginationModule,
|
|
1242
|
+
FormsModule,
|
|
1243
|
+
MarkdownPipe,
|
|
1244
|
+
],
|
|
1245
|
+
providers: [NzMessageService],
|
|
1246
|
+
exports: [NgxAgentChatComponent],
|
|
1247
|
+
}]
|
|
1248
|
+
}] });
|
|
1249
|
+
|
|
1250
|
+
/**
|
|
1251
|
+
* Generated bundle index. Do not edit.
|
|
1252
|
+
*/
|
|
1253
|
+
|
|
1254
|
+
export { AgentChatModule, NgxAgentChatComponent };
|
|
1255
|
+
//# sourceMappingURL=qqsl-agent-chat.mjs.map
|