@bidding-group/ai-chat 1.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 ADDED
@@ -0,0 +1,67 @@
1
+ # @bidding-group/ai-chat
2
+
3
+ 可独立使用的 Vue 3 AI 聊天插件,基于 SSE 流式响应,支持会话管理、图片上传、Token 用量统计。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pnpm add @bidding-group/ai-chat
9
+ ```
10
+
11
+ ## 使用
12
+
13
+ ### 1. 注册插件
14
+
15
+ ```ts
16
+ import { createApp } from 'vue'
17
+ import { createPinia } from 'pinia'
18
+ import AiChat from '@bidding-group/ai-chat'
19
+ import '@bidding-group/ai-chat/style.css'
20
+
21
+ const app = createApp(App)
22
+ app.use(createPinia())
23
+ app.use(AiChat, {
24
+ baseUrl: 'https://your-ai-service.com',
25
+ getToken: () => localStorage.getItem('token') || '',
26
+ getTenantId: () => 'your-tenant-id',
27
+ })
28
+ app.mount('#app')
29
+ ```
30
+
31
+ ### 2. 在模板中使用
32
+
33
+ ```vue
34
+ <template>
35
+ <AiChat />
36
+ </template>
37
+ ```
38
+
39
+ ## 配置项 (AiChatOptions)
40
+
41
+ | 参数 | 类型 | 必填 | 说明 |
42
+ |------|------|------|------|
43
+ | `baseUrl` | `string` | 是 | AI 服务地址 |
44
+ | `getToken` | `() => string` | 否 | 获取 JWT Token |
45
+ | `getTenantId` | `() => string` | 否 | 获取租户 ID |
46
+ | `getEnabled` | `() => boolean` | 否 | 宿主侧开关控制 |
47
+
48
+ ## 功能特性
49
+
50
+ - SSE 流式聊天响应
51
+ - 会话管理(创建/删除/分页)
52
+ - 图片上传(粘贴/点击,最多 4 张)
53
+ - Token 用量统计
54
+ - AI 思考过程展示 (reasoning_content)
55
+ - 可拖拽调整面板宽度
56
+ - 全屏模式
57
+ - 移动端适配
58
+
59
+ ## Peer Dependencies
60
+
61
+ - `vue >= 3.5.0`
62
+ - `pinia >= 2.0.0`
63
+ - `element-plus >= 2.0.0`
64
+
65
+ ## License
66
+
67
+ MIT
@@ -0,0 +1 @@
1
+ .ai-chat-float .float-btn[data-v-b3f66cd5]{width:50px;height:50px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#667eea,#764ba2);box-shadow:0 4px 15px #667eea66;transition:all .3s ease;color:#fff}.ai-chat-float .float-btn.is-active[data-v-b3f66cd5]{background:linear-gradient(135deg,#764ba2,#667eea);box-shadow:0 2px 8px #667eea4d}.ai-chat-float .float-btn[data-v-b3f66cd5]:hover{transform:scale(1.1);box-shadow:0 6px 20px #667eea80}.ai-chat-float .float-btn[data-v-b3f66cd5]:active{transform:scale(.95)}.ai-chat-float.is-mobile .float-btn[data-v-b3f66cd5]{width:56px;height:56px}.message-item[data-v-5ed8bd4d]{display:flex;gap:12px;margin-bottom:16px;padding:0 12px}.message-item.user[data-v-5ed8bd4d]{flex-direction:row-reverse}.message-item.user .content[data-v-5ed8bd4d]{align-items:flex-end}.message-item.user .content .text[data-v-5ed8bd4d]{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff}.message-item.user .avatar[data-v-5ed8bd4d]{background:#667eea}.message-item.assistant .avatar[data-v-5ed8bd4d]{background:#10a37f}.message-item .avatar[data-v-5ed8bd4d]{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0}.message-item .content[data-v-5ed8bd4d]{display:flex;flex-direction:column;max-width:80%}.message-item .content .message-images[data-v-5ed8bd4d]{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.message-item .content .message-images .message-image[data-v-5ed8bd4d]{max-width:200px;max-height:200px;border-radius:8px;object-fit:cover}.message-item .content .text[data-v-5ed8bd4d]{padding:12px 16px;background:#f5f5f5;border-radius:12px;line-height:1.6;word-break:break-word}.message-item .content .text[data-v-5ed8bd4d] .code-block{background:#1e1e1e;color:#d4d4d4;padding:12px;border-radius:6px;overflow-x:auto;margin:8px 0;font-family:Consolas,monospace;font-size:13px}.message-item .content .text[data-v-5ed8bd4d] .inline-code{background:#0000001a;padding:2px 6px;border-radius:4px;font-family:Consolas,monospace;font-size:13px}.message-item .content .loading-placeholder[data-v-5ed8bd4d]{padding:12px 16px;background:#f5f5f5;border-radius:12px;color:#999;font-style:italic}.message-item .content .time[data-v-5ed8bd4d]{font-size:12px;color:#999;margin-top:4px}.slide-enter-active[data-v-95d944a2],.slide-leave-active[data-v-95d944a2]{transition:transform .3s ease}.slide-enter-from[data-v-95d944a2],.slide-leave-to[data-v-95d944a2]{transform:translate(100%)}.fade-enter-active[data-v-95d944a2],.fade-leave-active[data-v-95d944a2]{transition:opacity .2s}.fade-enter-from[data-v-95d944a2],.fade-leave-to[data-v-95d944a2]{opacity:0}.ai-panel-overlay[data-v-95d944a2]{position:fixed;inset:0;background:#0000001a;z-index:9999}.ai-chat-panel[data-v-95d944a2]{position:fixed!important;top:0!important;height:100vh!important;background:#fff;box-shadow:-4px 0 24px #0000001f;z-index:10000!important;display:flex!important;flex-direction:row!important;transition:width .3s ease;bottom:auto!important}.ai-chat-panel.full-screen[data-v-95d944a2]{width:100%!important;box-shadow:none}.ai-chat-panel.full-screen .resize-handle[data-v-95d944a2]{display:none}.ai-chat-panel.is-mobile[data-v-95d944a2]{left:0!important;right:0!important;width:100%!important}.resize-handle[data-v-95d944a2]{position:relative;width:6px;height:100%;cursor:ew-resize;display:flex;align-items:center;justify-content:center;transition:background .2s}.resize-handle[data-v-95d944a2]:hover{background:#667eea1a}.resize-handle:hover .resize-bar[data-v-95d944a2]{background:#667eea}.resize-handle .resize-bar[data-v-95d944a2]{width:3px;height:40px;background:#ddd;border-radius:2px;transition:background .2s}.resize-handle .width-tooltip[data-v-95d944a2]{position:absolute;top:50%;left:20px;transform:translateY(-50%);padding:4px 8px;background:#000c;color:#fff;font-size:12px;border-radius:4px;white-space:nowrap;pointer-events:none}.panel-container[data-v-95d944a2]{flex:1;display:flex;flex-direction:column;overflow:hidden}.panel-header[data-v-95d944a2]{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #eee;background:#fafafa}.panel-header .header-left[data-v-95d944a2]{display:flex;align-items:center;gap:12px}.panel-header .header-left .title[data-v-95d944a2]{font-size:16px;font-weight:600;color:#333}.panel-header .header-left .token-usage[data-v-95d944a2]{font-size:12px;color:#666;background:#f0f0f0;padding:2px 8px;border-radius:10px;cursor:help}.panel-header .header-left .token-usage .limit-warning[data-v-95d944a2]{color:#f56c6c;font-weight:500}.panel-header .header-left .preset-btns[data-v-95d944a2]{display:flex;gap:4px}.panel-header .header-right[data-v-95d944a2]{display:flex;align-items:center;gap:8px}.chat-container[data-v-95d944a2]{flex:1;display:flex;flex-direction:column;overflow:hidden}.session-list[data-v-95d944a2]{height:100%;padding:16px}.session-list .session-header[data-v-95d944a2]{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;font-weight:500}.session-list .session-item[data-v-95d944a2]{padding:12px;border-radius:8px;cursor:pointer;margin-bottom:8px;background:#f5f5f5;transition:all .2s}.session-list .session-item[data-v-95d944a2]:hover{background:#e8e8e8}.session-list .session-item.active[data-v-95d944a2]{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff}.session-list .session-item.active .session-meta[data-v-95d944a2]{color:#fffc}.session-list .session-item .session-title[data-v-95d944a2]{font-weight:500;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.session-list .session-item .session-meta[data-v-95d944a2]{font-size:12px;color:#999;display:flex;justify-content:space-between;align-items:center}.session-list .session-load-more[data-v-95d944a2]{display:flex;align-items:center;justify-content:center;gap:6px;padding:12px 0;font-size:12px;color:#aaa}.session-list .session-load-more.session-no-more[data-v-95d944a2]{color:#ccc}.session-list .session-load-more .el-icon[data-v-95d944a2]{font-size:14px}.messages-area[data-v-95d944a2]{flex:1;overflow:hidden}.messages-area .messages-content[data-v-95d944a2]{padding:16px 0;min-height:100%}.pending-images[data-v-95d944a2]{display:flex;flex-wrap:wrap;gap:8px;padding:8px 16px;border-top:1px solid #f0f0f0}.pending-images .pending-image-wrap[data-v-95d944a2]{position:relative}.pending-images .pending-image-wrap .pending-image[data-v-95d944a2]{width:60px;height:60px;object-fit:cover;border-radius:6px}.pending-images .pending-image-wrap .image-index[data-v-95d944a2]{position:absolute;top:4px;left:4px;padding:2px 6px;background:#0009;color:#fff;font-size:11px;border-radius:10px}.pending-images .pending-image-wrap .remove-img[data-v-95d944a2]{position:absolute;top:-8px;right:-8px;padding:2px}.typing-indicator[data-v-95d944a2]{display:flex;gap:4px;padding:12px 60px}.typing-indicator span[data-v-95d944a2]{width:8px;height:8px;background:#667eea;border-radius:50%;animation:typing-95d944a2 1.4s infinite}.typing-indicator span[data-v-95d944a2]:nth-child(2){animation-delay:.2s}.typing-indicator span[data-v-95d944a2]:nth-child(3){animation-delay:.4s}@keyframes typing-95d944a2{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-8px);opacity:1}}.input-area[data-v-95d944a2]{display:flex;gap:8px;padding:12px 16px;border-top:1px solid #eee;background:#fff;align-items:flex-end}.input-area[data-v-95d944a2] .el-textarea__inner{border-radius:20px;padding:8px 16px}.ai-disclaimer[data-v-95d944a2]{text-align:center;font-size:11px;color:#bbb;padding:4px 0 6px;background:#fff}@media(max-width:767px){.panel-header[data-v-95d944a2]{padding:10px 12px}.panel-header .header-left[data-v-95d944a2]{gap:8px}.panel-header .header-left .title[data-v-95d944a2]{font-size:14px}.panel-header .header-left .preset-btns[data-v-95d944a2]{display:none}.panel-header .header-right[data-v-95d944a2]{gap:4px}.panel-header .header-right .el-button[data-v-95d944a2]{padding:4px}.input-area[data-v-95d944a2]{padding:8px 12px}.input-area[data-v-95d944a2] .el-textarea__inner{padding:6px 12px}}.cgs-ai-chat-root[data-v-e2e91206]{position:fixed;z-index:10000}
@@ -0,0 +1,856 @@
1
+ import { defineComponent as J, ref as b, computed as p, onMounted as we, onUnmounted as Te, resolveComponent as x, openBlock as o, createElementBlock as c, normalizeStyle as be, normalizeClass as W, createVNode as i, withCtx as l, createElementVNode as d, unref as r, createBlock as G, Fragment as A, renderList as X, createCommentVNode as S, toDisplayString as C, watch as fe, nextTick as pe, Transition as ee, withModifiers as te, createTextVNode as se, withKeys as Je } from "vue";
2
+ import { ChatDotRound as Be, Monitor as je, User as qe, Grid as Ke, Menu as Ye, FullScreen as Ze, Plus as Qe, Close as _e, Loading as et, List as tt, Promotion as st } from "@element-plus/icons-vue";
3
+ import { defineStore as nt } from "pinia";
4
+ import { ElScrollbar as ke, ElMessage as H } from "element-plus";
5
+ let ie = null;
6
+ function it(t) {
7
+ ie = t;
8
+ }
9
+ function Me() {
10
+ if (!ie)
11
+ throw new Error("[@cgs/ai-chat] 插件未安装,请先调用 app.use(AiChat, options)");
12
+ return ie;
13
+ }
14
+ async function U(t, e, g) {
15
+ const h = Me(), a = {
16
+ "Content-Type": "application/json"
17
+ };
18
+ h.getToken && (a["x-access-token"] = h.getToken()), h.getTenantId && (a["X-Tenant-Id"] = String(h.getTenantId()));
19
+ const k = await fetch(`${h.baseUrl}${e}`, {
20
+ method: t,
21
+ headers: a,
22
+ body: g !== void 0 ? JSON.stringify(g) : void 0
23
+ });
24
+ if (!k.ok)
25
+ throw new Error(`HTTP ${k.status}: ${e}`);
26
+ const I = (await k.text()).replace(/:\s*(\d{16,})/g, ':"$1"');
27
+ return JSON.parse(I).data;
28
+ }
29
+ const ot = () => U("POST", "/session/create"), ye = (t) => U("POST", "/session/page", t).then((e) => ({
30
+ list: e?.records ?? [],
31
+ total: e?.total ?? 0
32
+ })), at = (t) => U("GET", `/session/${t}/messages`), lt = (t) => U("DELETE", `/session/${t}`), rt = () => U("GET", "/usage/today");
33
+ async function ct(t) {
34
+ const e = Me(), g = {
35
+ "Content-Type": "application/json"
36
+ };
37
+ e.getToken && (g["x-access-token"] = e.getToken()), e.getTenantId && (g["X-Tenant-Id"] = String(e.getTenantId()));
38
+ const h = await fetch(`${e.baseUrl}/chat/stream`, {
39
+ method: "POST",
40
+ headers: g,
41
+ body: JSON.stringify(t),
42
+ signal: t.signal
43
+ // 支持 AbortSignal
44
+ });
45
+ if (!h.ok)
46
+ throw new Error(`流式请求失败: HTTP ${h.status}`);
47
+ return h;
48
+ }
49
+ const Ce = "cgs-ai-chat-state";
50
+ function dt() {
51
+ try {
52
+ const t = localStorage.getItem(Ce);
53
+ return t ? JSON.parse(t) : {};
54
+ } catch {
55
+ return {};
56
+ }
57
+ }
58
+ function ut(t) {
59
+ try {
60
+ localStorage.setItem(
61
+ Ce,
62
+ JSON.stringify({
63
+ currentSessionId: t.currentSessionId,
64
+ panelWidth: t.panelWidth,
65
+ isFullScreen: t.isFullScreen
66
+ })
67
+ );
68
+ } catch {
69
+ }
70
+ }
71
+ const Se = 15, ne = dt(), xe = nt("cgs-ai-chat", {
72
+ state: () => ({
73
+ visible: !1,
74
+ currentSessionId: ne.currentSessionId ?? null,
75
+ sessions: [],
76
+ sessionPage: 1,
77
+ sessionTotal: 0,
78
+ sessionLoadingMore: !1,
79
+ messages: [],
80
+ loading: !1,
81
+ sending: !1,
82
+ panelWidth: ne.panelWidth ?? (typeof window < "u" ? Math.max(320, Math.round(window.innerWidth * 0.25)) : 420),
83
+ isFullScreen: ne.isFullScreen ?? !1,
84
+ tokenUsage: null
85
+ }),
86
+ actions: {
87
+ toggleVisible() {
88
+ this.visible = !this.visible;
89
+ },
90
+ setVisible(t) {
91
+ this.visible = t;
92
+ },
93
+ setCurrentSession(t, e = !0) {
94
+ this.currentSessionId = t, this._persist(), this.messages = [], t && e && this.loadMessages(t);
95
+ },
96
+ addMessage(t) {
97
+ this.messages.push(t);
98
+ },
99
+ /**
100
+ * 追加最后一条 AI 消息内容(流式场景)
101
+ * 使用 for 逆序 + 对象替换,避免 ES2023 findLast + 响应式问题
102
+ */
103
+ updateLastAssistantMessage(t) {
104
+ for (let e = this.messages.length - 1; e >= 0; e--)
105
+ if (this.messages[e].role === "assistant") {
106
+ this.messages[e] = {
107
+ ...this.messages[e],
108
+ content: (this.messages[e].content || "") + t
109
+ };
110
+ break;
111
+ }
112
+ },
113
+ async loadSessions() {
114
+ this.loading = !0, this.sessionPage = 1;
115
+ try {
116
+ const t = await ye({ pageNum: 1, pageSize: Se });
117
+ this.sessions = t.list, this.sessionTotal = t.total;
118
+ } finally {
119
+ this.loading = !1;
120
+ }
121
+ },
122
+ /** 滚动加载更多会话(追加到列表末尾) */
123
+ async loadMoreSessions() {
124
+ if (!(this.sessionLoadingMore || this.sessions.length >= this.sessionTotal)) {
125
+ this.sessionLoadingMore = !0;
126
+ try {
127
+ const t = this.sessionPage + 1, e = await ye({ pageNum: t, pageSize: Se });
128
+ this.sessions = [...this.sessions, ...e.list], this.sessionTotal = e.total, this.sessionPage = t;
129
+ } finally {
130
+ this.sessionLoadingMore = !1;
131
+ }
132
+ }
133
+ },
134
+ async loadMessages(t) {
135
+ this.loading = !0;
136
+ try {
137
+ const e = await at(t);
138
+ this.currentSessionId === t && (this.messages = e);
139
+ } finally {
140
+ this.loading = !1;
141
+ }
142
+ },
143
+ async createSession() {
144
+ try {
145
+ const t = await ot();
146
+ return await this.loadSessions(), t;
147
+ } catch (t) {
148
+ return console.error("[@cgs/ai-chat] 创建会话失败", t), null;
149
+ }
150
+ },
151
+ async deleteSession(t) {
152
+ try {
153
+ await lt(t), await this.loadSessions(), this.currentSessionId === t && (this.currentSessionId = null, this.messages = [], this._persist());
154
+ } catch (e) {
155
+ console.error("[@cgs/ai-chat] 删除会话失败", e);
156
+ }
157
+ },
158
+ setSending(t) {
159
+ this.sending = t;
160
+ },
161
+ clearMessages() {
162
+ this.messages = [];
163
+ },
164
+ setPanelWidth(t) {
165
+ const g = Math.floor(window.innerWidth * 0.9);
166
+ this.panelWidth = Math.max(320, Math.min(g, t)), this.isFullScreen = !1, this._persist();
167
+ },
168
+ toggleFullScreen() {
169
+ this.isFullScreen = !this.isFullScreen, this._persist();
170
+ },
171
+ async loadTokenUsage() {
172
+ try {
173
+ this.tokenUsage = await rt();
174
+ } catch (t) {
175
+ console.error("[@cgs/ai-chat] 加载 Token 用量失败", t);
176
+ }
177
+ },
178
+ updateTokenUsage(t) {
179
+ this.tokenUsage && (this.tokenUsage.usedTokens += t, this.tokenUsage.limitTokens > 0 && (this.tokenUsage.remainingTokens = Math.max(
180
+ 0,
181
+ this.tokenUsage.limitTokens - this.tokenUsage.usedTokens
182
+ ), this.tokenUsage.limitReached = this.tokenUsage.remainingTokens <= 0), this.tokenUsage.requestCount++);
183
+ },
184
+ // 内部方法:持久化关键状态到 localStorage
185
+ _persist() {
186
+ ut(this.$state);
187
+ }
188
+ },
189
+ getters: {
190
+ isVisible: (t) => t.visible,
191
+ getCurrentSessionId: (t) => t.currentSessionId,
192
+ // string | null
193
+ getSessions: (t) => t.sessions,
194
+ getMessages: (t) => t.messages,
195
+ isLoading: (t) => t.loading,
196
+ isSending: (t) => t.sending,
197
+ /** 是否还有更多会话可加载 */
198
+ sessionHasMore: (t) => t.sessions.length < t.sessionTotal
199
+ }
200
+ }), gt = /* @__PURE__ */ J({
201
+ __name: "AiChatFloat",
202
+ setup(t) {
203
+ const e = xe(), g = () => e.toggleVisible(), h = b(!1), a = () => {
204
+ h.value = window.innerWidth < 768;
205
+ }, k = p(() => h.value ? {
206
+ position: "fixed",
207
+ right: "50%",
208
+ bottom: "24px",
209
+ transform: "translateX(50%)",
210
+ zIndex: "1000"
211
+ } : {
212
+ position: "fixed",
213
+ right: "24px",
214
+ bottom: "120px",
215
+ zIndex: "9990"
216
+ });
217
+ return we(() => {
218
+ a(), window.addEventListener("resize", a);
219
+ }), Te(() => {
220
+ window.removeEventListener("resize", a);
221
+ }), (y, I) => {
222
+ const $ = x("el-icon"), j = x("el-tooltip");
223
+ return o(), c("div", {
224
+ class: W(["ai-chat-float", { "is-mobile": h.value }]),
225
+ style: be(k.value),
226
+ onClick: g
227
+ }, [
228
+ i(j, {
229
+ content: "AI助手",
230
+ placement: "left",
231
+ "show-after": 300
232
+ }, {
233
+ default: l(() => [
234
+ d("div", {
235
+ class: W(["float-btn", { "is-active": r(e).visible }])
236
+ }, [
237
+ i($, { size: 24 }, {
238
+ default: l(() => [
239
+ i(r(Be))
240
+ ]),
241
+ _: 1
242
+ })
243
+ ], 2)
244
+ ]),
245
+ _: 1
246
+ })
247
+ ], 6);
248
+ };
249
+ }
250
+ }), B = (t, e) => {
251
+ const g = t.__vccOpts || t;
252
+ for (const [h, a] of e)
253
+ g[h] = a;
254
+ return g;
255
+ }, Ae = /* @__PURE__ */ B(gt, [["__scopeId", "data-v-b3f66cd5"]]), ht = { class: "avatar" }, mt = { class: "content" }, vt = {
256
+ key: 0,
257
+ class: "message-images"
258
+ }, ft = ["src"], pt = ["innerHTML"], _t = ["innerHTML"], kt = {
259
+ key: 1,
260
+ class: "loading-placeholder"
261
+ }, yt = { class: "time" }, St = /* @__PURE__ */ J({
262
+ __name: "AiChatMessage",
263
+ props: {
264
+ message: {}
265
+ },
266
+ setup(t) {
267
+ const e = t, g = p(() => {
268
+ let a = e.message.content || "";
269
+ return a = a.replace(/</g, "&lt;").replace(/>/g, "&gt;"), a = a.replace(/```(\w*)\n([\s\S]*?)```/g, '<pre class="code-block"><code>$2</code></pre>'), a = a.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>'), a = a.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>"), a = a.replace(/\n/g, "<br>"), a;
270
+ }), h = (a) => a ? new Date(a).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" }) : "";
271
+ return (a, k) => {
272
+ const y = x("el-icon");
273
+ return o(), c("div", {
274
+ class: W(["message-item", [t.message.role]])
275
+ }, [
276
+ d("div", ht, [
277
+ t.message.role === "assistant" ? (o(), G(y, {
278
+ key: 0,
279
+ size: 20
280
+ }, {
281
+ default: l(() => [
282
+ i(r(je))
283
+ ]),
284
+ _: 1
285
+ })) : (o(), G(y, {
286
+ key: 1,
287
+ size: 20
288
+ }, {
289
+ default: l(() => [
290
+ i(r(qe))
291
+ ]),
292
+ _: 1
293
+ }))
294
+ ]),
295
+ d("div", mt, [
296
+ t.message.role === "user" ? (o(), c(A, { key: 0 }, [
297
+ t.message.images && t.message.images.length > 0 ? (o(), c("div", vt, [
298
+ (o(!0), c(A, null, X(t.message.images, (I, $) => (o(), c("img", {
299
+ key: $,
300
+ src: I,
301
+ class: "message-image",
302
+ alt: "图片"
303
+ }, null, 8, ft))), 128))
304
+ ])) : S("", !0),
305
+ t.message.content && t.message.content !== "[图片]" ? (o(), c("div", {
306
+ key: 1,
307
+ class: "text",
308
+ innerHTML: g.value
309
+ }, null, 8, pt)) : S("", !0)
310
+ ], 64)) : (o(), c(A, { key: 1 }, [
311
+ t.message.content ? (o(), c("div", {
312
+ key: 0,
313
+ class: "text",
314
+ innerHTML: g.value
315
+ }, null, 8, _t)) : (o(), c("div", kt, "正在思考..."))
316
+ ], 64)),
317
+ d("div", yt, C(h(t.message.createTime)), 1)
318
+ ])
319
+ ], 2);
320
+ };
321
+ }
322
+ }), wt = /* @__PURE__ */ B(St, [["__scopeId", "data-v-5ed8bd4d"]]), Tt = {
323
+ key: 0,
324
+ class: "width-tooltip"
325
+ }, bt = { class: "panel-container" }, Mt = { class: "panel-header" }, Ct = { class: "header-left" }, xt = {
326
+ key: 0,
327
+ class: "token-usage"
328
+ }, At = {
329
+ key: 1,
330
+ class: "preset-btns"
331
+ }, It = { class: "header-right" }, Et = { class: "chat-container" }, Lt = {
332
+ key: 0,
333
+ class: "session-list"
334
+ }, $t = { class: "session-header" }, Wt = ["onClick"], zt = { class: "session-title" }, Rt = { class: "session-meta" }, Ut = {
335
+ key: 0,
336
+ class: "session-load-more"
337
+ }, Pt = {
338
+ key: 1,
339
+ class: "session-load-more session-no-more"
340
+ }, Ft = { class: "messages-area" }, Ot = { class: "messages-content" }, Dt = {
341
+ key: 0,
342
+ class: "typing-indicator"
343
+ }, Nt = {
344
+ key: 0,
345
+ class: "pending-images"
346
+ }, Vt = { class: "image-index" }, Ht = ["src"], Xt = { class: "input-area" }, Gt = 4, Jt = 0.25, Bt = 0.38, jt = /* @__PURE__ */ J({
347
+ __name: "AiChatDrawer",
348
+ setup(t) {
349
+ const e = xe();
350
+ let g = new AbortController();
351
+ const h = p({
352
+ get: () => e.visible,
353
+ set: (s) => e.setVisible(s)
354
+ }), a = p(() => e.panelWidth), k = p(() => e.isFullScreen), y = b(!1), I = () => {
355
+ y.value = window.innerWidth < 768;
356
+ }, $ = p(() => {
357
+ const s = {
358
+ position: "fixed",
359
+ top: "0",
360
+ height: "100vh",
361
+ zIndex: "10000",
362
+ display: "flex",
363
+ flexDirection: "row"
364
+ };
365
+ return y.value ? {
366
+ ...s,
367
+ left: "0",
368
+ right: "0",
369
+ width: "100%"
370
+ } : k.value ? { ...s, right: "0", width: "100%" } : { ...s, right: "0", width: `${a.value}px` };
371
+ }), j = p(() => {
372
+ const s = e.tokenUsage;
373
+ return s ? s.limitTokens === 0 ? `${s.usedTokens} tokens` : `${s.usedTokens}/${s.limitTokens}` : "";
374
+ }), Ee = p(() => {
375
+ const s = e.tokenUsage;
376
+ return s ? s.limitTokens === 0 ? `今日已使用 ${s.usedTokens} tokens(不限制)` : s.limitReached ? "今日配额已用完,请明天再试" : `今日已使用 ${s.usedTokens}/${s.limitTokens} tokens,剩余 ${s.remainingTokens}` : "";
377
+ }), z = b(""), R = b(!1), M = b([]), P = b(), oe = b(), F = b(window.innerWidth), ae = () => {
378
+ F.value = window.innerWidth, I();
379
+ }, le = p(() => y.value ? window.innerWidth : Math.max(320, Math.round(F.value * Jt))), re = p(() => y.value ? window.innerWidth : Math.max(420, Math.round(F.value * Bt))), q = p(() => a.value / F.value), Le = p(() => !k.value && q.value <= 0.3), $e = p(() => !k.value && q.value > 0.3 && q.value <= 0.45), We = p(() => `窄 (${le.value}px)`), ze = p(() => `宽 (${re.value}px)`), E = b(!1), O = b(0), D = b(0);
380
+ let _ = null;
381
+ const Re = (s) => {
382
+ E.value = !0, O.value = s.clientX, D.value = a.value, document.addEventListener("mousemove", K), document.addEventListener("mouseup", Z), document.body.style.cursor = "ew-resize", document.body.style.userSelect = "none";
383
+ }, Ue = (s) => {
384
+ s.touches.length === 1 && (E.value = !0, O.value = s.touches[0].clientX, D.value = a.value, document.addEventListener("touchmove", Y), document.addEventListener("touchend", Q));
385
+ }, K = (s) => {
386
+ E.value && (_ && cancelAnimationFrame(_), _ = requestAnimationFrame(() => {
387
+ const n = D.value + (O.value - s.clientX), u = Math.max(320, Math.min(window.innerWidth / 2, n));
388
+ e.setPanelWidth(u);
389
+ }));
390
+ }, Y = (s) => {
391
+ !E.value || s.touches.length !== 1 || (_ && cancelAnimationFrame(_), _ = requestAnimationFrame(() => {
392
+ const n = D.value + (O.value - s.touches[0].clientX), u = Math.max(320, Math.min(window.innerWidth / 2, n));
393
+ e.setPanelWidth(u);
394
+ }));
395
+ }, Z = () => {
396
+ E.value = !1, _ && (cancelAnimationFrame(_), _ = null), document.removeEventListener("mousemove", K), document.removeEventListener("mouseup", Z), document.body.style.cursor = "", document.body.style.userSelect = "";
397
+ }, Q = () => {
398
+ E.value = !1, _ && (cancelAnimationFrame(_), _ = null), document.removeEventListener("touchmove", Y), document.removeEventListener("touchend", Q);
399
+ }, ce = (s) => e.setPanelWidth(s), Pe = () => e.toggleFullScreen(), de = () => e.setVisible(!1), Fe = async (s) => {
400
+ const n = s.clipboardData?.items;
401
+ if (n)
402
+ for (let u = 0; u < n.length; u++) {
403
+ const v = n[u];
404
+ if (v.type.startsWith("image/")) {
405
+ if (s.preventDefault(), M.value.length >= Gt) {
406
+ H.warning("最多只能上传4张图片");
407
+ return;
408
+ }
409
+ const f = v.getAsFile();
410
+ if (f) {
411
+ const L = await Oe(f);
412
+ M.value.push(L);
413
+ }
414
+ }
415
+ }
416
+ }, Oe = (s) => new Promise((n) => {
417
+ const u = new FileReader();
418
+ u.onload = (v) => n(v.target.result), u.readAsDataURL(s);
419
+ }), De = (s) => s?.length > 30 ? s.substring(0, 30) + "..." : s, Ne = ({ scrollTop: s }) => {
420
+ const n = oe.value?.wrapRef;
421
+ n && s + n.clientHeight >= n.scrollHeight - 150 && e.loadMoreSessions();
422
+ }, ue = () => {
423
+ P.value?.wrapRef && (P.value.wrapRef.scrollTop = P.value.wrapRef.scrollHeight);
424
+ };
425
+ fe(() => e.messages.length, () => pe(ue)), fe(h, (s) => {
426
+ s && (e.setCurrentSession(null), e.loadSessions(), e.loadTokenUsage());
427
+ });
428
+ const Ve = async () => {
429
+ const s = await e.createSession();
430
+ s ? (e.setCurrentSession(s, !1), R.value = !1) : H.error("创建对话失败,请检查网络或重新登录");
431
+ }, He = (s) => {
432
+ e.setCurrentSession(s.id), R.value = !1;
433
+ }, Xe = async (s) => {
434
+ await e.deleteSession(s);
435
+ }, ge = async () => {
436
+ const s = z.value.trim(), n = [...M.value];
437
+ if (!s && n.length === 0 || e.sending) return;
438
+ z.value = "", M.value = [], e.setSending(!0), g = new AbortController();
439
+ let u = e.currentSessionId;
440
+ u || (u = await e.createSession(), u && e.setCurrentSession(u, !1));
441
+ const v = {
442
+ id: Date.now(),
443
+ sessionId: u ?? "",
444
+ role: "user",
445
+ content: s || "[图片]",
446
+ images: n.length > 0 ? n : void 0,
447
+ createTime: (/* @__PURE__ */ new Date()).toISOString()
448
+ };
449
+ e.addMessage(v);
450
+ const f = {
451
+ id: Date.now() + 1,
452
+ sessionId: u ?? "",
453
+ role: "assistant",
454
+ content: "",
455
+ createTime: (/* @__PURE__ */ new Date()).toISOString()
456
+ };
457
+ e.addMessage(f);
458
+ try {
459
+ const N = (await ct({
460
+ sessionId: u ?? void 0,
461
+ // string | undefined
462
+ message: s || "[图片]",
463
+ images: n.length > 0 ? n : void 0,
464
+ signal: g.signal
465
+ // 传递 signal 以支持取消
466
+ })).body?.getReader(), m = new TextDecoder();
467
+ if (N) {
468
+ let w = "";
469
+ for (; ; ) {
470
+ const { done: he, value: Ge } = await N.read();
471
+ if (he) break;
472
+ w += m.decode(Ge, { stream: !0 });
473
+ const me = w.split(`
474
+ `);
475
+ w = me.pop() ?? "";
476
+ let ve = "";
477
+ for (const V of me)
478
+ if (V.startsWith("event:"))
479
+ ve = V.slice(6).trim();
480
+ else if (V.startsWith("data:"))
481
+ try {
482
+ const T = JSON.parse(V.slice(5).trim());
483
+ if (ve === "error" || T.error) {
484
+ e.updateLastAssistantMessage(T.error || "服务异常,请重试。"), H.error(T.error || "服务异常,请重试");
485
+ break;
486
+ }
487
+ !T.done && T.content && (e.updateLastAssistantMessage(T.content), pe(ue)), T.sessionId && !e.currentSessionId && e.setCurrentSession(T.sessionId), T.done && T.usage && e.updateTokenUsage(T.usage.totalTokens);
488
+ } catch {
489
+ }
490
+ }
491
+ }
492
+ } catch (L) {
493
+ L.name === "AbortError" ? console.log("[@cgs/ai-chat] 请求已取消") : (console.error("[@cgs/ai-chat] 发送消息失败", L), e.updateLastAssistantMessage("抱歉,发生了错误,请重试。"), H.error({
494
+ message: "消息发送失败,请检查网络连接",
495
+ duration: 3e3,
496
+ showClose: !0
497
+ }));
498
+ } finally {
499
+ e.setSending(!1), e.loadSessions();
500
+ }
501
+ };
502
+ return we(() => {
503
+ window.addEventListener("resize", ae), I();
504
+ }), Te(() => {
505
+ g.abort(), window.removeEventListener("resize", ae), document.removeEventListener("mousemove", K), document.removeEventListener("mouseup", Z), document.removeEventListener("touchmove", Y), document.removeEventListener("touchend", Q), _ && cancelAnimationFrame(_);
506
+ }), (s, n) => {
507
+ const u = x("el-tooltip"), v = x("el-icon"), f = x("el-button"), L = x("el-empty"), N = x("el-input");
508
+ return o(), c(A, null, [
509
+ i(ee, { name: "slide" }, {
510
+ default: l(() => [
511
+ h.value ? (o(), c("div", {
512
+ key: 0,
513
+ class: W(["ai-chat-panel", { "full-screen": k.value, "is-mobile": y.value }]),
514
+ style: be($.value)
515
+ }, [
516
+ y.value ? S("", !0) : (o(), c("div", {
517
+ key: 0,
518
+ class: "resize-handle",
519
+ onMousedown: Re,
520
+ onTouchstart: te(Ue, ["prevent"])
521
+ }, [
522
+ n[5] || (n[5] = d("div", { class: "resize-bar" }, null, -1)),
523
+ i(ee, { name: "fade" }, {
524
+ default: l(() => [
525
+ E.value ? (o(), c("div", Tt, C(Math.round(a.value)) + "px ", 1)) : S("", !0)
526
+ ]),
527
+ _: 1
528
+ })
529
+ ], 32)),
530
+ d("div", bt, [
531
+ d("div", Mt, [
532
+ d("div", Ct, [
533
+ n[6] || (n[6] = d("span", { class: "title" }, "AI助手", -1)),
534
+ r(e).tokenUsage ? (o(), c("div", xt, [
535
+ i(u, {
536
+ content: Ee.value,
537
+ placement: "bottom"
538
+ }, {
539
+ default: l(() => [
540
+ d("span", {
541
+ class: W({ "limit-warning": r(e).tokenUsage.limitReached })
542
+ }, C(j.value), 3)
543
+ ]),
544
+ _: 1
545
+ }, 8, ["content"])
546
+ ])) : S("", !0),
547
+ y.value ? S("", !0) : (o(), c("div", At, [
548
+ i(u, {
549
+ content: We.value,
550
+ placement: "bottom"
551
+ }, {
552
+ default: l(() => [
553
+ i(f, {
554
+ size: "small",
555
+ type: Le.value ? "primary" : "default",
556
+ link: "",
557
+ onClick: n[0] || (n[0] = (m) => ce(le.value))
558
+ }, {
559
+ default: l(() => [
560
+ i(v, null, {
561
+ default: l(() => [
562
+ i(r(Ke))
563
+ ]),
564
+ _: 1
565
+ })
566
+ ]),
567
+ _: 1
568
+ }, 8, ["type"])
569
+ ]),
570
+ _: 1
571
+ }, 8, ["content"]),
572
+ i(u, {
573
+ content: ze.value,
574
+ placement: "bottom"
575
+ }, {
576
+ default: l(() => [
577
+ i(f, {
578
+ size: "small",
579
+ type: $e.value ? "primary" : "default",
580
+ link: "",
581
+ onClick: n[1] || (n[1] = (m) => ce(re.value))
582
+ }, {
583
+ default: l(() => [
584
+ i(v, null, {
585
+ default: l(() => [
586
+ i(r(Ye))
587
+ ]),
588
+ _: 1
589
+ })
590
+ ]),
591
+ _: 1
592
+ }, 8, ["type"])
593
+ ]),
594
+ _: 1
595
+ }, 8, ["content"]),
596
+ i(u, {
597
+ content: k.value ? "退出全屏" : "全屏",
598
+ placement: "bottom"
599
+ }, {
600
+ default: l(() => [
601
+ i(f, {
602
+ size: "small",
603
+ type: k.value ? "primary" : "default",
604
+ link: "",
605
+ onClick: Pe
606
+ }, {
607
+ default: l(() => [
608
+ i(v, null, {
609
+ default: l(() => [
610
+ i(r(Ze))
611
+ ]),
612
+ _: 1
613
+ })
614
+ ]),
615
+ _: 1
616
+ }, 8, ["type"])
617
+ ]),
618
+ _: 1
619
+ }, 8, ["content"])
620
+ ]))
621
+ ]),
622
+ d("div", It, [
623
+ i(f, {
624
+ type: "primary",
625
+ link: "",
626
+ onClick: Ve
627
+ }, {
628
+ default: l(() => [
629
+ i(v, null, {
630
+ default: l(() => [
631
+ i(r(Qe))
632
+ ]),
633
+ _: 1
634
+ }),
635
+ n[7] || (n[7] = se("新对话 ", -1))
636
+ ]),
637
+ _: 1
638
+ }),
639
+ i(f, {
640
+ type: "default",
641
+ link: "",
642
+ onClick: de
643
+ }, {
644
+ default: l(() => [
645
+ i(v, null, {
646
+ default: l(() => [
647
+ i(r(_e))
648
+ ]),
649
+ _: 1
650
+ })
651
+ ]),
652
+ _: 1
653
+ })
654
+ ])
655
+ ]),
656
+ d("div", Et, [
657
+ R.value ? (o(), c("div", Lt, [
658
+ d("div", $t, [
659
+ n[9] || (n[9] = d("span", null, "历史对话", -1)),
660
+ i(f, {
661
+ type: "primary",
662
+ link: "",
663
+ size: "small",
664
+ onClick: n[2] || (n[2] = (m) => R.value = !1)
665
+ }, {
666
+ default: l(() => [...n[8] || (n[8] = [
667
+ se("返回聊天", -1)
668
+ ])]),
669
+ _: 1
670
+ })
671
+ ]),
672
+ i(r(ke), {
673
+ ref_key: "sessionScrollbarRef",
674
+ ref: oe,
675
+ height: (y.value, "calc(100vh - 200px)"),
676
+ onScroll: Ne
677
+ }, {
678
+ default: l(() => [
679
+ (o(!0), c(A, null, X(r(e).sessions, (m) => (o(), c("div", {
680
+ key: m.id,
681
+ class: W(["session-item", { active: m.id === r(e).currentSessionId }]),
682
+ onClick: (w) => He(m)
683
+ }, [
684
+ i(u, {
685
+ content: m.title,
686
+ placement: "left",
687
+ "show-after": 500
688
+ }, {
689
+ default: l(() => [
690
+ d("div", zt, C(De(m.title)), 1)
691
+ ]),
692
+ _: 2
693
+ }, 1032, ["content"]),
694
+ d("div", Rt, [
695
+ d("span", null, C(m.messageCount) + "条消息", 1),
696
+ i(f, {
697
+ type: "danger",
698
+ link: "",
699
+ size: "small",
700
+ onClick: te((w) => Xe(m.id), ["stop"])
701
+ }, {
702
+ default: l(() => [...n[10] || (n[10] = [
703
+ se("删除", -1)
704
+ ])]),
705
+ _: 1
706
+ }, 8, ["onClick"])
707
+ ])
708
+ ], 10, Wt))), 128)),
709
+ r(e).sessionLoadingMore ? (o(), c("div", Ut, [
710
+ i(v, { class: "is-loading" }, {
711
+ default: l(() => [
712
+ i(r(et))
713
+ ]),
714
+ _: 1
715
+ }),
716
+ n[11] || (n[11] = d("span", null, "加载中...", -1))
717
+ ])) : r(e).sessions.length > 0 && !r(e).sessionHasMore ? (o(), c("div", Pt, " 共 " + C(r(e).sessionTotal) + " 条对话 ", 1)) : S("", !0),
718
+ r(e).sessions.length === 0 ? (o(), G(L, {
719
+ key: 2,
720
+ description: "暂无历史对话"
721
+ })) : S("", !0)
722
+ ]),
723
+ _: 1
724
+ }, 8, ["height"])
725
+ ])) : (o(), c(A, { key: 1 }, [
726
+ d("div", Ft, [
727
+ i(r(ke), {
728
+ ref_key: "scrollbarRef",
729
+ ref: P,
730
+ height: "100%"
731
+ }, {
732
+ default: l(() => [
733
+ d("div", Ot, [
734
+ (o(!0), c(A, null, X(r(e).messages, (m, w) => (o(), G(wt, {
735
+ key: w,
736
+ message: m
737
+ }, null, 8, ["message"]))), 128)),
738
+ r(e).sending ? (o(), c("div", Dt, [...n[12] || (n[12] = [
739
+ d("span", null, null, -1),
740
+ d("span", null, null, -1),
741
+ d("span", null, null, -1)
742
+ ])])) : S("", !0)
743
+ ])
744
+ ]),
745
+ _: 1
746
+ }, 512)
747
+ ]),
748
+ M.value.length > 0 ? (o(), c("div", Nt, [
749
+ (o(!0), c(A, null, X(M.value, (m, w) => (o(), c("div", {
750
+ key: w,
751
+ class: "pending-image-wrap"
752
+ }, [
753
+ d("div", Vt, C(w + 1) + "/" + C(M.value.length), 1),
754
+ d("img", {
755
+ src: m,
756
+ class: "pending-image",
757
+ alt: "待发送图片"
758
+ }, null, 8, Ht),
759
+ i(f, {
760
+ type: "danger",
761
+ circle: "",
762
+ size: "small",
763
+ class: "remove-img",
764
+ onClick: (he) => M.value.splice(w, 1)
765
+ }, {
766
+ default: l(() => [
767
+ i(v, null, {
768
+ default: l(() => [
769
+ i(r(_e))
770
+ ]),
771
+ _: 1
772
+ })
773
+ ]),
774
+ _: 1
775
+ }, 8, ["onClick"])
776
+ ]))), 128))
777
+ ])) : S("", !0),
778
+ d("div", Xt, [
779
+ i(f, {
780
+ type: "primary",
781
+ link: "",
782
+ onClick: n[3] || (n[3] = (m) => R.value = !0)
783
+ }, {
784
+ default: l(() => [
785
+ i(v, null, {
786
+ default: l(() => [
787
+ i(r(tt))
788
+ ]),
789
+ _: 1
790
+ })
791
+ ]),
792
+ _: 1
793
+ }),
794
+ i(N, {
795
+ modelValue: z.value,
796
+ "onUpdate:modelValue": n[4] || (n[4] = (m) => z.value = m),
797
+ type: "textarea",
798
+ rows: 2,
799
+ maxlength: "2000",
800
+ "show-word-limit": "",
801
+ placeholder: "输入消息... (Enter 发送,Shift+Enter 换行,Ctrl+V 粘贴截图)",
802
+ resize: "none",
803
+ disabled: r(e).sending || r(e).tokenUsage?.limitReached,
804
+ onKeydown: Je(te(ge, ["exact", "prevent"]), ["enter"]),
805
+ onPaste: Fe
806
+ }, null, 8, ["modelValue", "disabled", "onKeydown"]),
807
+ i(f, {
808
+ type: "primary",
809
+ icon: r(st),
810
+ circle: "",
811
+ loading: r(e).sending,
812
+ disabled: !z.value.trim() && M.value.length === 0 || r(e).sending || r(e).tokenUsage?.limitReached,
813
+ onClick: ge
814
+ }, null, 8, ["icon", "loading", "disabled"])
815
+ ]),
816
+ n[13] || (n[13] = d("div", { class: "ai-disclaimer" }, "内容由AI生成,请仔细甄别", -1))
817
+ ], 64))
818
+ ])
819
+ ])
820
+ ], 6)) : S("", !0)
821
+ ]),
822
+ _: 1
823
+ }),
824
+ i(ee, { name: "fade" }, {
825
+ default: l(() => [
826
+ h.value ? (o(), c("div", {
827
+ key: 0,
828
+ class: "ai-panel-overlay",
829
+ onClick: de
830
+ })) : S("", !0)
831
+ ]),
832
+ _: 1
833
+ })
834
+ ], 64);
835
+ };
836
+ }
837
+ }), Ie = /* @__PURE__ */ B(jt, [["__scopeId", "data-v-95d944a2"]]), qt = { class: "cgs-ai-chat-root" }, Kt = /* @__PURE__ */ J({
838
+ __name: "AiChat",
839
+ setup(t) {
840
+ return (e, g) => (o(), c("div", qt, [
841
+ i(Ae),
842
+ i(Ie)
843
+ ]));
844
+ }
845
+ }), Yt = /* @__PURE__ */ B(Kt, [["__scopeId", "data-v-e2e91206"]]), ss = {
846
+ install(t, e) {
847
+ e?.baseUrl || console.warn("[@cgs/ai-chat] 警告:未提供 baseUrl,组件可能无法正常工作"), it(e), t.component("AiChat", Yt), t.component("AiChatFloat", Ae), t.component("AiChatDrawer", Ie);
848
+ }
849
+ };
850
+ export {
851
+ Ie as AiChatDrawer,
852
+ Ae as AiChatFloat,
853
+ wt as AiChatMessage,
854
+ ss as default,
855
+ xe as useAiStore
856
+ };
@@ -0,0 +1,2 @@
1
+ (function(h,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("@element-plus/icons-vue"),require("pinia"),require("element-plus")):typeof define=="function"&&define.amd?define(["exports","vue","@element-plus/icons-vue","pinia","element-plus"],e):(h=typeof globalThis<"u"?globalThis:h||self,e(h.AiChat={},h.Vue,h.ElementPlusIconsVue,h.Pinia,h.ElementPlus))})(this,(function(h,e,k,re,E){"use strict";let z=null;function ce(s){z=s}function j(){if(!z)throw new Error("[@cgs/ai-chat] 插件未安装,请先调用 app.use(AiChat, options)");return z}async function T(s,t,l){const r=j(),a={"Content-Type":"application/json"};r.getToken&&(a["x-access-token"]=r.getToken()),r.getTenantId&&(a["X-Tenant-Id"]=String(r.getTenantId()));const p=await fetch(`${r.baseUrl}${t}`,{method:s,headers:a,body:l!==void 0?JSON.stringify(l):void 0});if(!p.ok)throw new Error(`HTTP ${p.status}: ${t}`);const y=(await p.text()).replace(/:\s*(\d{16,})/g,':"$1"');return JSON.parse(y).data}const de=()=>T("POST","/session/create"),G=s=>T("POST","/session/page",s).then(t=>({list:t?.records??[],total:t?.total??0})),me=s=>T("GET",`/session/${s}/messages`),he=s=>T("DELETE",`/session/${s}`),ge=()=>T("GET","/usage/today");async function pe(s){const t=j(),l={"Content-Type":"application/json"};t.getToken&&(l["x-access-token"]=t.getToken()),t.getTenantId&&(l["X-Tenant-Id"]=String(t.getTenantId()));const r=await fetch(`${t.baseUrl}/chat/stream`,{method:"POST",headers:l,body:JSON.stringify(s),signal:s.signal});if(!r.ok)throw new Error(`流式请求失败: HTTP ${r.status}`);return r}const J="cgs-ai-chat-state";function fe(){try{const s=localStorage.getItem(J);return s?JSON.parse(s):{}}catch{return{}}}function ke(s){try{localStorage.setItem(J,JSON.stringify({currentSessionId:s.currentSessionId,panelWidth:s.panelWidth,isFullScreen:s.isFullScreen}))}catch{}}const K=15,W=fe(),F=re.defineStore("cgs-ai-chat",{state:()=>({visible:!1,currentSessionId:W.currentSessionId??null,sessions:[],sessionPage:1,sessionTotal:0,sessionLoadingMore:!1,messages:[],loading:!1,sending:!1,panelWidth:W.panelWidth??(typeof window<"u"?Math.max(320,Math.round(window.innerWidth*.25)):420),isFullScreen:W.isFullScreen??!1,tokenUsage:null}),actions:{toggleVisible(){this.visible=!this.visible},setVisible(s){this.visible=s},setCurrentSession(s,t=!0){this.currentSessionId=s,this._persist(),this.messages=[],s&&t&&this.loadMessages(s)},addMessage(s){this.messages.push(s)},updateLastAssistantMessage(s){for(let t=this.messages.length-1;t>=0;t--)if(this.messages[t].role==="assistant"){this.messages[t]={...this.messages[t],content:(this.messages[t].content||"")+s};break}},async loadSessions(){this.loading=!0,this.sessionPage=1;try{const s=await G({pageNum:1,pageSize:K});this.sessions=s.list,this.sessionTotal=s.total}finally{this.loading=!1}},async loadMoreSessions(){if(!(this.sessionLoadingMore||this.sessions.length>=this.sessionTotal)){this.sessionLoadingMore=!0;try{const s=this.sessionPage+1,t=await G({pageNum:s,pageSize:K});this.sessions=[...this.sessions,...t.list],this.sessionTotal=t.total,this.sessionPage=s}finally{this.sessionLoadingMore=!1}}},async loadMessages(s){this.loading=!0;try{const t=await me(s);this.currentSessionId===s&&(this.messages=t)}finally{this.loading=!1}},async createSession(){try{const s=await de();return await this.loadSessions(),s}catch(s){return console.error("[@cgs/ai-chat] 创建会话失败",s),null}},async deleteSession(s){try{await he(s),await this.loadSessions(),this.currentSessionId===s&&(this.currentSessionId=null,this.messages=[],this._persist())}catch(t){console.error("[@cgs/ai-chat] 删除会话失败",t)}},setSending(s){this.sending=s},clearMessages(){this.messages=[]},setPanelWidth(s){const l=Math.floor(window.innerWidth*.9);this.panelWidth=Math.max(320,Math.min(l,s)),this.isFullScreen=!1,this._persist()},toggleFullScreen(){this.isFullScreen=!this.isFullScreen,this._persist()},async loadTokenUsage(){try{this.tokenUsage=await ge()}catch(s){console.error("[@cgs/ai-chat] 加载 Token 用量失败",s)}},updateTokenUsage(s){this.tokenUsage&&(this.tokenUsage.usedTokens+=s,this.tokenUsage.limitTokens>0&&(this.tokenUsage.remainingTokens=Math.max(0,this.tokenUsage.limitTokens-this.tokenUsage.usedTokens),this.tokenUsage.limitReached=this.tokenUsage.remainingTokens<=0),this.tokenUsage.requestCount++)},_persist(){ke(this.$state)}},getters:{isVisible:s=>s.visible,getCurrentSessionId:s=>s.currentSessionId,getSessions:s=>s.sessions,getMessages:s=>s.messages,isLoading:s=>s.loading,isSending:s=>s.sending,sessionHasMore:s=>s.sessions.length<s.sessionTotal}}),ue=e.defineComponent({__name:"AiChatFloat",setup(s){const t=F(),l=()=>t.toggleVisible(),r=e.ref(!1),a=()=>{r.value=window.innerWidth<768},p=e.computed(()=>r.value?{position:"fixed",right:"50%",bottom:"24px",transform:"translateX(50%)",zIndex:"1000"}:{position:"fixed",right:"24px",bottom:"120px",zIndex:"9990"});return e.onMounted(()=>{a(),window.addEventListener("resize",a)}),e.onUnmounted(()=>{window.removeEventListener("resize",a)}),(f,y)=>{const N=e.resolveComponent("el-icon"),P=e.resolveComponent("el-tooltip");return e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["ai-chat-float",{"is-mobile":r.value}]),style:e.normalizeStyle(p.value),onClick:l},[e.createVNode(P,{content:"AI助手",placement:"left","show-after":300},{default:e.withCtx(()=>[e.createElementVNode("div",{class:e.normalizeClass(["float-btn",{"is-active":e.unref(t).visible}])},[e.createVNode(N,{size:24},{default:e.withCtx(()=>[e.createVNode(e.unref(k.ChatDotRound))]),_:1})],2)]),_:1})],6)}}}),B=(s,t)=>{const l=s.__vccOpts||s;for(const[r,a]of t)l[r]=a;return l},R=B(ue,[["__scopeId","data-v-b3f66cd5"]]),we={class:"avatar"},_e={class:"content"},ye={key:0,class:"message-images"},Se=["src"],Ce=["innerHTML"],Ee=["innerHTML"],Ne={key:1,class:"loading-placeholder"},Te={class:"time"},Y=B(e.defineComponent({__name:"AiChatMessage",props:{message:{}},setup(s){const t=s,l=e.computed(()=>{let a=t.message.content||"";return a=a.replace(/</g,"&lt;").replace(/>/g,"&gt;"),a=a.replace(/```(\w*)\n([\s\S]*?)```/g,'<pre class="code-block"><code>$2</code></pre>'),a=a.replace(/`([^`]+)`/g,'<code class="inline-code">$1</code>'),a=a.replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>"),a=a.replace(/\n/g,"<br>"),a}),r=a=>a?new Date(a).toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"}):"";return(a,p)=>{const f=e.resolveComponent("el-icon");return e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(["message-item",[s.message.role]])},[e.createElementVNode("div",we,[s.message.role==="assistant"?(e.openBlock(),e.createBlock(f,{key:0,size:20},{default:e.withCtx(()=>[e.createVNode(e.unref(k.Monitor))]),_:1})):(e.openBlock(),e.createBlock(f,{key:1,size:20},{default:e.withCtx(()=>[e.createVNode(e.unref(k.User))]),_:1}))]),e.createElementVNode("div",_e,[s.message.role==="user"?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[s.message.images&&s.message.images.length>0?(e.openBlock(),e.createElementBlock("div",ye,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(s.message.images,(y,N)=>(e.openBlock(),e.createElementBlock("img",{key:N,src:y,class:"message-image",alt:"图片"},null,8,Se))),128))])):e.createCommentVNode("",!0),s.message.content&&s.message.content!=="[图片]"?(e.openBlock(),e.createElementBlock("div",{key:1,class:"text",innerHTML:l.value},null,8,Ce)):e.createCommentVNode("",!0)],64)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[s.message.content?(e.openBlock(),e.createElementBlock("div",{key:0,class:"text",innerHTML:l.value},null,8,Ee)):(e.openBlock(),e.createElementBlock("div",Ne,"正在思考..."))],64)),e.createElementVNode("div",Te,e.toDisplayString(r(s.message.createTime)),1)])],2)}}}),[["__scopeId","data-v-5ed8bd4d"]]),Ve={key:0,class:"width-tooltip"},xe={class:"panel-container"},Be={class:"panel-header"},Me={class:"header-left"},be={key:0,class:"token-usage"},Ae={key:1,class:"preset-btns"},Ie={class:"header-right"},Le={class:"chat-container"},$e={key:0,class:"session-list"},ze={class:"session-header"},We=["onClick"],Fe={class:"session-title"},Re={class:"session-meta"},Ue={key:0,class:"session-load-more"},Pe={key:1,class:"session-load-more session-no-more"},De={class:"messages-area"},Oe={class:"messages-content"},He={key:0,class:"typing-indicator"},Xe={key:0,class:"pending-images"},qe={class:"image-index"},je=["src"],Ge={class:"input-area"},Je=4,Ke=.25,Ye=.38,U=B(e.defineComponent({__name:"AiChatDrawer",setup(s){const t=F();let l=new AbortController;const r=e.computed({get:()=>t.visible,set:n=>t.setVisible(n)}),a=e.computed(()=>t.panelWidth),p=e.computed(()=>t.isFullScreen),f=e.ref(!1),y=()=>{f.value=window.innerWidth<768},N=e.computed(()=>{const n={position:"fixed",top:"0",height:"100vh",zIndex:"10000",display:"flex",flexDirection:"row"};return f.value?{...n,left:"0",right:"0",width:"100%"}:p.value?{...n,right:"0",width:"100%"}:{...n,right:"0",width:`${a.value}px`}}),P=e.computed(()=>{const n=t.tokenUsage;return n?n.limitTokens===0?`${n.usedTokens} tokens`:`${n.usedTokens}/${n.limitTokens}`:""}),et=e.computed(()=>{const n=t.tokenUsage;return n?n.limitTokens===0?`今日已使用 ${n.usedTokens} tokens(不限制)`:n.limitReached?"今日配额已用完,请明天再试":`今日已使用 ${n.usedTokens}/${n.limitTokens} tokens,剩余 ${n.remainingTokens}`:""}),V=e.ref(""),x=e.ref(!1),_=e.ref([]),M=e.ref(),Z=e.ref(),b=e.ref(window.innerWidth),Q=()=>{b.value=window.innerWidth,y()},v=e.computed(()=>f.value?window.innerWidth:Math.max(320,Math.round(b.value*Ke))),ee=e.computed(()=>f.value?window.innerWidth:Math.max(420,Math.round(b.value*Ye))),D=e.computed(()=>a.value/b.value),tt=e.computed(()=>!p.value&&D.value<=.3),st=e.computed(()=>!p.value&&D.value>.3&&D.value<=.45),nt=e.computed(()=>`窄 (${v.value}px)`),ot=e.computed(()=>`宽 (${ee.value}px)`),S=e.ref(!1),A=e.ref(0),I=e.ref(0);let g=null;const at=n=>{S.value=!0,A.value=n.clientX,I.value=a.value,document.addEventListener("mousemove",O),document.addEventListener("mouseup",X),document.body.style.cursor="ew-resize",document.body.style.userSelect="none"},it=n=>{n.touches.length===1&&(S.value=!0,A.value=n.touches[0].clientX,I.value=a.value,document.addEventListener("touchmove",H),document.addEventListener("touchend",q))},O=n=>{S.value&&(g&&cancelAnimationFrame(g),g=requestAnimationFrame(()=>{const o=I.value+(A.value-n.clientX),i=Math.max(320,Math.min(window.innerWidth/2,o));t.setPanelWidth(i)}))},H=n=>{!S.value||n.touches.length!==1||(g&&cancelAnimationFrame(g),g=requestAnimationFrame(()=>{const o=I.value+(A.value-n.touches[0].clientX),i=Math.max(320,Math.min(window.innerWidth/2,o));t.setPanelWidth(i)}))},X=()=>{S.value=!1,g&&(cancelAnimationFrame(g),g=null),document.removeEventListener("mousemove",O),document.removeEventListener("mouseup",X),document.body.style.cursor="",document.body.style.userSelect=""},q=()=>{S.value=!1,g&&(cancelAnimationFrame(g),g=null),document.removeEventListener("touchmove",H),document.removeEventListener("touchend",q)},te=n=>t.setPanelWidth(n),lt=()=>t.toggleFullScreen(),se=()=>t.setVisible(!1),rt=async n=>{const o=n.clipboardData?.items;if(o)for(let i=0;i<o.length;i++){const d=o[i];if(d.type.startsWith("image/")){if(n.preventDefault(),_.value.length>=Je){E.ElMessage.warning("最多只能上传4张图片");return}const m=d.getAsFile();if(m){const C=await ct(m);_.value.push(C)}}}},ct=n=>new Promise(o=>{const i=new FileReader;i.onload=d=>o(d.target.result),i.readAsDataURL(n)}),dt=n=>n?.length>30?n.substring(0,30)+"...":n,mt=({scrollTop:n})=>{const o=Z.value?.wrapRef;o&&n+o.clientHeight>=o.scrollHeight-150&&t.loadMoreSessions()},ne=()=>{M.value?.wrapRef&&(M.value.wrapRef.scrollTop=M.value.wrapRef.scrollHeight)};e.watch(()=>t.messages.length,()=>e.nextTick(ne)),e.watch(r,n=>{n&&(t.setCurrentSession(null),t.loadSessions(),t.loadTokenUsage())});const ht=async()=>{const n=await t.createSession();n?(t.setCurrentSession(n,!1),x.value=!1):E.ElMessage.error("创建对话失败,请检查网络或重新登录")},gt=n=>{t.setCurrentSession(n.id),x.value=!1},pt=async n=>{await t.deleteSession(n)},oe=async()=>{const n=V.value.trim(),o=[..._.value];if(!n&&o.length===0||t.sending)return;V.value="",_.value=[],t.setSending(!0),l=new AbortController;let i=t.currentSessionId;i||(i=await t.createSession(),i&&t.setCurrentSession(i,!1));const d={id:Date.now(),sessionId:i??"",role:"user",content:n||"[图片]",images:o.length>0?o:void 0,createTime:new Date().toISOString()};t.addMessage(d);const m={id:Date.now()+1,sessionId:i??"",role:"assistant",content:"",createTime:new Date().toISOString()};t.addMessage(m);try{const L=(await pe({sessionId:i??void 0,message:n||"[图片]",images:o.length>0?o:void 0,signal:l.signal})).body?.getReader(),c=new TextDecoder;if(L){let u="";for(;;){const{done:ae,value:ft}=await L.read();if(ae)break;u+=c.decode(ft,{stream:!0});const ie=u.split(`
2
+ `);u=ie.pop()??"";let le="";for(const $ of ie)if($.startsWith("event:"))le=$.slice(6).trim();else if($.startsWith("data:"))try{const w=JSON.parse($.slice(5).trim());if(le==="error"||w.error){t.updateLastAssistantMessage(w.error||"服务异常,请重试。"),E.ElMessage.error(w.error||"服务异常,请重试");break}!w.done&&w.content&&(t.updateLastAssistantMessage(w.content),e.nextTick(ne)),w.sessionId&&!t.currentSessionId&&t.setCurrentSession(w.sessionId),w.done&&w.usage&&t.updateTokenUsage(w.usage.totalTokens)}catch{}}}}catch(C){C.name==="AbortError"?console.log("[@cgs/ai-chat] 请求已取消"):(console.error("[@cgs/ai-chat] 发送消息失败",C),t.updateLastAssistantMessage("抱歉,发生了错误,请重试。"),E.ElMessage.error({message:"消息发送失败,请检查网络连接",duration:3e3,showClose:!0}))}finally{t.setSending(!1),t.loadSessions()}};return e.onMounted(()=>{window.addEventListener("resize",Q),y()}),e.onUnmounted(()=>{l.abort(),window.removeEventListener("resize",Q),document.removeEventListener("mousemove",O),document.removeEventListener("mouseup",X),document.removeEventListener("touchmove",H),document.removeEventListener("touchend",q),g&&cancelAnimationFrame(g)}),(n,o)=>{const i=e.resolveComponent("el-tooltip"),d=e.resolveComponent("el-icon"),m=e.resolveComponent("el-button"),C=e.resolveComponent("el-empty"),L=e.resolveComponent("el-input");return e.openBlock(),e.createElementBlock(e.Fragment,null,[e.createVNode(e.Transition,{name:"slide"},{default:e.withCtx(()=>[r.value?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["ai-chat-panel",{"full-screen":p.value,"is-mobile":f.value}]),style:e.normalizeStyle(N.value)},[f.value?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("div",{key:0,class:"resize-handle",onMousedown:at,onTouchstart:e.withModifiers(it,["prevent"])},[o[5]||(o[5]=e.createElementVNode("div",{class:"resize-bar"},null,-1)),e.createVNode(e.Transition,{name:"fade"},{default:e.withCtx(()=>[S.value?(e.openBlock(),e.createElementBlock("div",Ve,e.toDisplayString(Math.round(a.value))+"px ",1)):e.createCommentVNode("",!0)]),_:1})],32)),e.createElementVNode("div",xe,[e.createElementVNode("div",Be,[e.createElementVNode("div",Me,[o[6]||(o[6]=e.createElementVNode("span",{class:"title"},"AI助手",-1)),e.unref(t).tokenUsage?(e.openBlock(),e.createElementBlock("div",be,[e.createVNode(i,{content:et.value,placement:"bottom"},{default:e.withCtx(()=>[e.createElementVNode("span",{class:e.normalizeClass({"limit-warning":e.unref(t).tokenUsage.limitReached})},e.toDisplayString(P.value),3)]),_:1},8,["content"])])):e.createCommentVNode("",!0),f.value?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("div",Ae,[e.createVNode(i,{content:nt.value,placement:"bottom"},{default:e.withCtx(()=>[e.createVNode(m,{size:"small",type:tt.value?"primary":"default",link:"",onClick:o[0]||(o[0]=c=>te(v.value))},{default:e.withCtx(()=>[e.createVNode(d,null,{default:e.withCtx(()=>[e.createVNode(e.unref(k.Grid))]),_:1})]),_:1},8,["type"])]),_:1},8,["content"]),e.createVNode(i,{content:ot.value,placement:"bottom"},{default:e.withCtx(()=>[e.createVNode(m,{size:"small",type:st.value?"primary":"default",link:"",onClick:o[1]||(o[1]=c=>te(ee.value))},{default:e.withCtx(()=>[e.createVNode(d,null,{default:e.withCtx(()=>[e.createVNode(e.unref(k.Menu))]),_:1})]),_:1},8,["type"])]),_:1},8,["content"]),e.createVNode(i,{content:p.value?"退出全屏":"全屏",placement:"bottom"},{default:e.withCtx(()=>[e.createVNode(m,{size:"small",type:p.value?"primary":"default",link:"",onClick:lt},{default:e.withCtx(()=>[e.createVNode(d,null,{default:e.withCtx(()=>[e.createVNode(e.unref(k.FullScreen))]),_:1})]),_:1},8,["type"])]),_:1},8,["content"])]))]),e.createElementVNode("div",Ie,[e.createVNode(m,{type:"primary",link:"",onClick:ht},{default:e.withCtx(()=>[e.createVNode(d,null,{default:e.withCtx(()=>[e.createVNode(e.unref(k.Plus))]),_:1}),o[7]||(o[7]=e.createTextVNode("新对话 ",-1))]),_:1}),e.createVNode(m,{type:"default",link:"",onClick:se},{default:e.withCtx(()=>[e.createVNode(d,null,{default:e.withCtx(()=>[e.createVNode(e.unref(k.Close))]),_:1})]),_:1})])]),e.createElementVNode("div",Le,[x.value?(e.openBlock(),e.createElementBlock("div",$e,[e.createElementVNode("div",ze,[o[9]||(o[9]=e.createElementVNode("span",null,"历史对话",-1)),e.createVNode(m,{type:"primary",link:"",size:"small",onClick:o[2]||(o[2]=c=>x.value=!1)},{default:e.withCtx(()=>[...o[8]||(o[8]=[e.createTextVNode("返回聊天",-1)])]),_:1})]),e.createVNode(e.unref(E.ElScrollbar),{ref_key:"sessionScrollbarRef",ref:Z,height:(f.value,"calc(100vh - 200px)"),onScroll:mt},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(t).sessions,c=>(e.openBlock(),e.createElementBlock("div",{key:c.id,class:e.normalizeClass(["session-item",{active:c.id===e.unref(t).currentSessionId}]),onClick:u=>gt(c)},[e.createVNode(i,{content:c.title,placement:"left","show-after":500},{default:e.withCtx(()=>[e.createElementVNode("div",Fe,e.toDisplayString(dt(c.title)),1)]),_:2},1032,["content"]),e.createElementVNode("div",Re,[e.createElementVNode("span",null,e.toDisplayString(c.messageCount)+"条消息",1),e.createVNode(m,{type:"danger",link:"",size:"small",onClick:e.withModifiers(u=>pt(c.id),["stop"])},{default:e.withCtx(()=>[...o[10]||(o[10]=[e.createTextVNode("删除",-1)])]),_:1},8,["onClick"])])],10,We))),128)),e.unref(t).sessionLoadingMore?(e.openBlock(),e.createElementBlock("div",Ue,[e.createVNode(d,{class:"is-loading"},{default:e.withCtx(()=>[e.createVNode(e.unref(k.Loading))]),_:1}),o[11]||(o[11]=e.createElementVNode("span",null,"加载中...",-1))])):e.unref(t).sessions.length>0&&!e.unref(t).sessionHasMore?(e.openBlock(),e.createElementBlock("div",Pe," 共 "+e.toDisplayString(e.unref(t).sessionTotal)+" 条对话 ",1)):e.createCommentVNode("",!0),e.unref(t).sessions.length===0?(e.openBlock(),e.createBlock(C,{key:2,description:"暂无历史对话"})):e.createCommentVNode("",!0)]),_:1},8,["height"])])):(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("div",De,[e.createVNode(e.unref(E.ElScrollbar),{ref_key:"scrollbarRef",ref:M,height:"100%"},{default:e.withCtx(()=>[e.createElementVNode("div",Oe,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(t).messages,(c,u)=>(e.openBlock(),e.createBlock(Y,{key:u,message:c},null,8,["message"]))),128)),e.unref(t).sending?(e.openBlock(),e.createElementBlock("div",He,[...o[12]||(o[12]=[e.createElementVNode("span",null,null,-1),e.createElementVNode("span",null,null,-1),e.createElementVNode("span",null,null,-1)])])):e.createCommentVNode("",!0)])]),_:1},512)]),_.value.length>0?(e.openBlock(),e.createElementBlock("div",Xe,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(_.value,(c,u)=>(e.openBlock(),e.createElementBlock("div",{key:u,class:"pending-image-wrap"},[e.createElementVNode("div",qe,e.toDisplayString(u+1)+"/"+e.toDisplayString(_.value.length),1),e.createElementVNode("img",{src:c,class:"pending-image",alt:"待发送图片"},null,8,je),e.createVNode(m,{type:"danger",circle:"",size:"small",class:"remove-img",onClick:ae=>_.value.splice(u,1)},{default:e.withCtx(()=>[e.createVNode(d,null,{default:e.withCtx(()=>[e.createVNode(e.unref(k.Close))]),_:1})]),_:1},8,["onClick"])]))),128))])):e.createCommentVNode("",!0),e.createElementVNode("div",Ge,[e.createVNode(m,{type:"primary",link:"",onClick:o[3]||(o[3]=c=>x.value=!0)},{default:e.withCtx(()=>[e.createVNode(d,null,{default:e.withCtx(()=>[e.createVNode(e.unref(k.List))]),_:1})]),_:1}),e.createVNode(L,{modelValue:V.value,"onUpdate:modelValue":o[4]||(o[4]=c=>V.value=c),type:"textarea",rows:2,maxlength:"2000","show-word-limit":"",placeholder:"输入消息... (Enter 发送,Shift+Enter 换行,Ctrl+V 粘贴截图)",resize:"none",disabled:e.unref(t).sending||e.unref(t).tokenUsage?.limitReached,onKeydown:e.withKeys(e.withModifiers(oe,["exact","prevent"]),["enter"]),onPaste:rt},null,8,["modelValue","disabled","onKeydown"]),e.createVNode(m,{type:"primary",icon:e.unref(k.Promotion),circle:"",loading:e.unref(t).sending,disabled:!V.value.trim()&&_.value.length===0||e.unref(t).sending||e.unref(t).tokenUsage?.limitReached,onClick:oe},null,8,["icon","loading","disabled"])]),o[13]||(o[13]=e.createElementVNode("div",{class:"ai-disclaimer"},"内容由AI生成,请仔细甄别",-1))],64))])])],6)):e.createCommentVNode("",!0)]),_:1}),e.createVNode(e.Transition,{name:"fade"},{default:e.withCtx(()=>[r.value?(e.openBlock(),e.createElementBlock("div",{key:0,class:"ai-panel-overlay",onClick:se})):e.createCommentVNode("",!0)]),_:1})],64)}}}),[["__scopeId","data-v-95d944a2"]]),Ze={class:"cgs-ai-chat-root"},Qe=B(e.defineComponent({__name:"AiChat",setup(s){return(t,l)=>(e.openBlock(),e.createElementBlock("div",Ze,[e.createVNode(R),e.createVNode(U)]))}}),[["__scopeId","data-v-e2e91206"]]),ve={install(s,t){t?.baseUrl||console.warn("[@cgs/ai-chat] 警告:未提供 baseUrl,组件可能无法正常工作"),ce(t),s.component("AiChat",Qe),s.component("AiChatFloat",R),s.component("AiChatDrawer",U)}};h.AiChatDrawer=U,h.AiChatFloat=R,h.AiChatMessage=Y,h.default=ve,h.useAiStore=F,Object.defineProperties(h,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@bidding-group/ai-chat",
3
+ "version": "1.0.1",
4
+ "description": "CGS AI Chat — 可独立使用的 Vue 3 AI 聊天插件",
5
+ "type": "module",
6
+ "main": "./dist/ai-chat.umd.cjs",
7
+ "module": "./dist/ai-chat.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/ai-chat.js",
13
+ "require": "./dist/ai-chat.umd.cjs"
14
+ },
15
+ "./dist/style.css": "./dist/ai-chat.css",
16
+ "./style.css": "./dist/ai-chat.css",
17
+ "./dist/ai-chat.css": "./dist/ai-chat.css"
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "scripts": {
23
+ "dev": "vite build --watch",
24
+ "build": "vue-tsc --noEmit && vite build",
25
+ "type-check": "vue-tsc --noEmit",
26
+ "prepare": "pnpm run build"
27
+ },
28
+ "peerDependencies": {
29
+ "vue": "^3.5.0",
30
+ "pinia": "^2.0.0",
31
+ "element-plus": "^2.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.4.0",
35
+ "vite": "^7.0.0",
36
+ "@vitejs/plugin-vue": "^6.0.0",
37
+ "vue-tsc": "^2.0.0",
38
+ "vue": "^3.5.0",
39
+ "pinia": "^2.0.0",
40
+ "element-plus": "^2.7.0",
41
+ "@element-plus/icons-vue": "^2.3.0",
42
+ "sass": "^1.70.0"
43
+ }
44
+ }