@kernelift/ai-chat 1.0.1-beta.0

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,711 @@
1
+ # Vue3 AI 聊天框组件使用手册
2
+
3
+ 配合的请求SSE库:[Sse-Client](./SSE-Client.md)
4
+
5
+ ## 目录
6
+
7
+ - [概述](#概述)
8
+ - [安装与导入](#安装与导入)
9
+ - [组件构成](#组件构成)
10
+ - [主容器组件](#主容器组件)
11
+ - [子组件详解](#子组件详解)
12
+ - [事件处理](#事件处理)
13
+ - [样式定制](#样式定制)
14
+ - [使用示例](#使用示例)
15
+ - [API 参考](#api-参考)
16
+
17
+ ## 概述
18
+
19
+ Vue3 AI 聊天框组件是一个功能完整的聊天界面解决方案,支持流式对话、消息气泡、侧边栏历史记录、主题切换等特性。
20
+
21
+ ### 主要特性
22
+
23
+ - 💬 **实时对话** - 支持流式消息显示
24
+ - 📱 **响应式设计** - 适配桌面端和移动端
25
+ - 🎨 **主题系统** - 支持亮色/暗色主题切换
26
+ - 📚 **历史记录** - 侧边栏对话历史管理
27
+ - 🛠️ **工具集成** - 支持深度思考、联网搜索等功能
28
+ - 🎯 **可定制** - 丰富的插槽和配置选项
29
+
30
+ ## 安装与导入
31
+
32
+ ### 安装依赖
33
+
34
+ ```bash
35
+ # 安装核心依赖
36
+ npm install @kernelift/icons @kernelift/utils @kernelift/markdown element-plus
37
+ ```
38
+
39
+ ### 组件导入
40
+
41
+ ```typescript
42
+ // 全局注册
43
+ import { createApp } from 'vue';
44
+ import ChatContainer from '@kernelift/ai-chat';
45
+ import '@kernelift/ai-chat/style.css';
46
+
47
+ const app = createApp(App);
48
+ app.component('AIChatBox', ChatContainer);
49
+ ```
50
+
51
+ ```vue
52
+ <!-- 局部导入 -->
53
+ <script setup>
54
+ import {
55
+ ChatContainer,
56
+ ChatBubble,
57
+ ChatSender,
58
+ ChatSidebar,
59
+ ChatHeader,
60
+ ThinkingProcess
61
+ } from '@kernelift/ai-chat';
62
+ import type { ChatMessage, ChatRecord, BubbleEvent } from '@kernelift/ai-chat';
63
+ </script>
64
+ ```
65
+
66
+ ## 组件构成
67
+
68
+ ### 组件结构
69
+
70
+ ```
71
+ ChatContainer (主容器)
72
+ ├── ChatSidebar (侧边栏)
73
+ ├── ChatHeader (头部)
74
+ ├── ChatBubble (消息气泡)
75
+ ├── ThinkingProcess (思考过程)
76
+ ├── ChatSender (发送器)
77
+ └── 工作区 (可选)
78
+ ```
79
+
80
+ ## 主容器组件
81
+
82
+ ### 基本用法
83
+
84
+ ```vue
85
+ <template>
86
+ <ChatContainer
87
+ v-model:messages="messages"
88
+ v-model:inputText="inputText"
89
+ v-model:loading="loading"
90
+ :records="chatRecords"
91
+ @send="handleSend"
92
+ @bubble-event="handleBubbleEvent"
93
+ />
94
+ </template>
95
+
96
+ <script setup lang="ts">
97
+ import { ref } from 'vue';
98
+ import type { ChatMessage, ChatRecord, BubbleEvent } from '@kernelift/ai-chat';
99
+
100
+ const messages = ref<ChatMessage[]>([]);
101
+ const inputText = ref('');
102
+ const loading = ref(false);
103
+ const chatRecords = ref<ChatRecord[]>([]);
104
+
105
+ const handleSend = (text: string) => {
106
+ // 处理发送消息
107
+ console.log('发送消息:', text);
108
+ };
109
+
110
+ const handleBubbleEvent = (eventName: BubbleEvent, data: ChatMessage) => {
111
+ // 处理气泡事件
112
+ console.log('气泡事件:', eventName, data);
113
+ };
114
+ </script>
115
+ ```
116
+
117
+ ### 完整配置示例
118
+
119
+ ```vue
120
+ <template>
121
+ <ChatContainer
122
+ v-model:messages="messages"
123
+ v-model:inputText="inputText"
124
+ v-model:loading="loading"
125
+ :records="chatRecords"
126
+ :has-header="true"
127
+ :has-theme-mode="true"
128
+ :has-sender-tools="true"
129
+ :show-workspace="true"
130
+ :theme-mode="currentTheme"
131
+ :primary-color="primaryColor"
132
+ :default-record-id="defaultRecordId"
133
+ @send="handleSend"
134
+ @bubble-event="handleBubbleEvent"
135
+ @change-record="handleRecordChange"
136
+ @change-theme="handleThemeChange"
137
+ @change-collapse="handleCollapseChange"
138
+ @click-logo="handleLogoClick"
139
+ >
140
+ <!-- 自定义侧边栏 Logo -->
141
+ <template #logo>
142
+ <div class="custom-logo">
143
+ <img src="/logo.png" alt="Logo" />
144
+ <span>我的AI助手</span>
145
+ </div>
146
+ </template>
147
+
148
+ <!-- 自定义消息头部 -->
149
+ <template #bubble-header="{ data }">
150
+ <div class="custom-header">
151
+ <span>{{ formatTime(data.timestamp) }}</span>
152
+ </div>
153
+ </template>
154
+
155
+ <!-- 自定义发送工具 -->
156
+ <template #sender-tools>
157
+ <el-button @click="handleAttachment">
158
+ <IconRender icon="material-symbols:attach-file" />
159
+ 附件
160
+ </el-button>
161
+ </template>
162
+
163
+ <!-- 自定义工作区 -->
164
+ <template #workspace="{ record }">
165
+ <div class="workspace-content">
166
+ <h3>当前对话: {{ record?.name }}</h3>
167
+ <!-- 工作区内容 -->
168
+ </div>
169
+ </template>
170
+ </ChatContainer>
171
+ </template>
172
+ ```
173
+
174
+ ## 子组件详解
175
+
176
+ ### ChatBubble - 消息气泡
177
+
178
+ 消息气泡组件支持多种消息类型和交互操作。
179
+
180
+ ```vue
181
+ <template>
182
+ <ChatBubble
183
+ v-model="message"
184
+ :is-last="isLastMessage"
185
+ :markdown-class="customMarkdownClass"
186
+ @bubble-event="handleBubbleEvent"
187
+ >
188
+ <!-- 自定义消息头部 -->
189
+ <template #header>
190
+ <div class="message-time">
191
+ {{ formatTime(message.timestamp) }}
192
+ </div>
193
+ </template>
194
+
195
+ <!-- 自定义思考过程头部 -->
196
+ <template #thinking-header>
197
+ <div class="thinking-label">推理过程</div>
198
+ </template>
199
+
200
+ <!-- 自定义操作按钮 -->
201
+ <template #bubble-event>
202
+ <el-button @click="handleCustomAction(message)"> 自定义操作 </el-button>
203
+ </template>
204
+ </ChatBubble>
205
+ </template>
206
+ ```
207
+
208
+ ### ChatSender - 消息发送器
209
+
210
+ 发送器组件提供消息输入和工具按钮。
211
+
212
+ ```vue
213
+ <template>
214
+ <ChatSender
215
+ v-model="inputText"
216
+ :loading="isSending"
217
+ :has-thinking="true"
218
+ :has-net-search="true"
219
+ @send="handleSend"
220
+ >
221
+ <!-- 自定义工具 -->
222
+ <template #tools="{ value, loading }">
223
+ <el-tooltip content="清空输入">
224
+ <el-button :disabled="!value" @click="inputText = ''">
225
+ <IconRender icon="material-symbols:clear" />
226
+ </el-button>
227
+ </el-tooltip>
228
+ </template>
229
+ </ChatSender>
230
+ </template>
231
+ ```
232
+
233
+ ### ChatSidebar - 侧边栏
234
+
235
+ 侧边栏组件管理对话历史记录。
236
+
237
+ ```vue
238
+ <template>
239
+ <ChatSidebar
240
+ v-model="activeRecord"
241
+ :data="records"
242
+ :collapse="isCollapsed"
243
+ :theme-mode="theme"
244
+ @change="handleRecordChange"
245
+ @change-collapse="handleCollapse"
246
+ @change-theme="handleThemeChange"
247
+ @click-logo="handleLogoClick"
248
+ >
249
+ <!-- 自定义 Logo -->
250
+ <template #logo>
251
+ <div class="brand-logo">
252
+ <img src="/brand-logo.png" alt="Brand" />
253
+ <span>AI对话平台</span>
254
+ </div>
255
+ </template>
256
+ </ChatSidebar>
257
+ </template>
258
+ ```
259
+
260
+ ### ThinkingProcess - 思考过程
261
+
262
+ 展示AI的思考推理过程。
263
+
264
+ ```vue
265
+ <template>
266
+ <ThinkingProcess
267
+ v-model:collapse="isThoughtCollapsed"
268
+ :data="thoughtContent"
269
+ :loading="isThinking"
270
+ :markdown-class="thoughtStyle"
271
+ >
272
+ <!-- 自定义头部 -->
273
+ <template #header>
274
+ <div class="thought-title">
275
+ <IconRender icon="material-symbols:psychology" />
276
+ AI思考过程
277
+ </div>
278
+ </template>
279
+ </ThinkingProcess>
280
+ </template>
281
+ ```
282
+
283
+ ## 事件处理
284
+
285
+ ### 消息事件
286
+
287
+ ```typescript
288
+ // 消息发送
289
+ const handleSend = (text: string) => {
290
+ // 添加用户消息
291
+ messages.value.push({
292
+ id: generateId(),
293
+ role: 'user',
294
+ content: text,
295
+ timestamp: Date.now()
296
+ });
297
+
298
+ // 调用AI接口
299
+ callAI(text).then((response) => {
300
+ // 处理AI响应
301
+ });
302
+ };
303
+
304
+ // 气泡交互事件
305
+ const handleBubbleEvent = (eventName: BubbleEvent, message: ChatMessage) => {
306
+ switch (eventName) {
307
+ case 'like':
308
+ // 点赞处理
309
+ message.isLiked = true;
310
+ message.isDisliked = false;
311
+ break;
312
+ case 'dislike':
313
+ // 点踩处理
314
+ message.isLiked = false;
315
+ message.isDisliked = true;
316
+ break;
317
+ case 'copy':
318
+ // 复制消息
319
+ navigator.clipboard.writeText(message.content);
320
+ ElMessage.success('已复制到剪贴板');
321
+ break;
322
+ case 'reload':
323
+ // 重新生成
324
+ handleRegenerate(message);
325
+ break;
326
+ case 'terminate':
327
+ // 终止生成
328
+ handleTerminate(message);
329
+ break;
330
+ case 'bookmark':
331
+ // 收藏消息
332
+ message.isBookmarked = !message.isBookmarked;
333
+ break;
334
+ }
335
+ };
336
+ ```
337
+
338
+ ### 流式消息处理
339
+
340
+ ```typescript
341
+ import { SSEClient } from './sse-client';
342
+
343
+ const handleStreamResponse = async (question: string) => {
344
+ const client = new SSEClient('your-token', 'https://api.example.com');
345
+
346
+ let currentMessage: ChatMessage = {
347
+ id: generateId(),
348
+ role: 'assistant',
349
+ content: '',
350
+ timestamp: Date.now(),
351
+ loading: true
352
+ };
353
+
354
+ messages.value.push(currentMessage);
355
+
356
+ const handlers = {
357
+ onContent: (content: string) => {
358
+ currentMessage.content += content;
359
+ },
360
+
361
+ onToolCallDelta: (data: any) => {
362
+ // 处理工具调用
363
+ if (!currentMessage.toolCalls) {
364
+ currentMessage.toolCalls = [];
365
+ }
366
+ currentMessage.toolCalls.push(data);
367
+ },
368
+
369
+ onComplete: (finalData: { content: string; toolCalls: any[] }) => {
370
+ currentMessage.content = finalData.content;
371
+ currentMessage.toolCalls = finalData.toolCalls;
372
+ currentMessage.loading = false;
373
+ },
374
+
375
+ onError: (error: Error) => {
376
+ console.error('流式响应错误:', error);
377
+ currentMessage.loading = false;
378
+ currentMessage.isError = true;
379
+ currentMessage.error = error.message;
380
+ }
381
+ };
382
+
383
+ await client.connect('/api/chat/stream', handlers, {
384
+ body: { question }
385
+ });
386
+ };
387
+ ```
388
+
389
+ ## 样式定制
390
+
391
+ ### CSS 变量定制
392
+
393
+ ```css
394
+ /* 在全局样式或组件样式中覆盖 */
395
+ :root {
396
+ --kl-chat-primary-color: #615ced;
397
+ --kl-chat-primary-rgb: 97, 92, 237;
398
+
399
+ /* Element Plus 主题色 */
400
+ --el-color-primary: var(--kl-chat-primary-color);
401
+ --el-color-primary-light-3: #8a86f1;
402
+ --el-color-primary-light-5: #b3b0f5;
403
+ }
404
+
405
+ /* 暗色主题 */
406
+ .dark {
407
+ --kl-chat-primary-color: #8a86f1;
408
+ --dark-background-color: #1a1a1a;
409
+ }
410
+ ```
411
+
412
+ ### SCSS 样式定制
413
+
414
+ ```scss
415
+ // 自定义主题
416
+ .kernelift-chat-container {
417
+ // 修改主容器样式
418
+ border-radius: 16px;
419
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
420
+
421
+ &__aside {
422
+ // 修改侧边栏样式
423
+ background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%);
424
+ }
425
+
426
+ &__messages-section {
427
+ // 修改消息区域样式
428
+ background-color: #fafbfc;
429
+ }
430
+ }
431
+
432
+ // 自定义消息气泡
433
+ .custom-chat-bubble {
434
+ .kernelift-chat-bubble__assistant {
435
+ background: linear-gradient(135deg, #ffffff 0%, #f0f4ff 100%);
436
+ border: 1px solid #e1e8ff;
437
+ }
438
+
439
+ .kernelift-chat-bubble__user {
440
+ &-content {
441
+ background: linear-gradient(135deg, var(--kl-chat-primary-color) 0%, #8a86f1 100%);
442
+ color: white;
443
+ }
444
+ }
445
+ }
446
+ ```
447
+
448
+ ## 使用示例
449
+
450
+ ### 完整聊天应用
451
+
452
+ ```vue
453
+ <template>
454
+ <div class="chat-app">
455
+ <ChatContainer
456
+ v-model:messages="messages"
457
+ v-model:inputText="inputText"
458
+ v-model:loading="loading"
459
+ :records="records"
460
+ :has-theme-mode="true"
461
+ :has-sender-tools="true"
462
+ :primary-color="brandColor"
463
+ @send="handleSend"
464
+ @bubble-event="handleBubbleEvent"
465
+ @change-theme="handleThemeChange"
466
+ >
467
+ <!-- 自定义头部 -->
468
+ <template #header="{ record }">
469
+ <div class="custom-header">
470
+ <h2>{{ record?.name || '新对话' }}</h2>
471
+ <div class="header-actions">
472
+ <el-button @click="exportChat">导出</el-button>
473
+ <el-button @click="clearChat">清空</el-button>
474
+ </div>
475
+ </div>
476
+ </template>
477
+
478
+ <!-- 自定义发送工具 -->
479
+ <template #sender-tools>
480
+ <div class="sender-tools">
481
+ <el-tooltip content="上传文件">
482
+ <el-button circle @click="handleFileUpload">
483
+ <IconRender icon="material-symbols:attach-file" />
484
+ </el-button>
485
+ </el-tooltip>
486
+ <el-tooltip content="语音输入">
487
+ <el-button circle @click="handleVoiceInput">
488
+ <IconRender icon="material-symbols:mic" />
489
+ </el-button>
490
+ </el-tooltip>
491
+ </div>
492
+ </template>
493
+
494
+ <!-- 自定义工作区 -->
495
+ <template #workspace="{ record }">
496
+ <ChatWorkspace :record="record" @analysis="handleAnalysis" />
497
+ </template>
498
+ </ChatContainer>
499
+ </div>
500
+ </template>
501
+
502
+ <script setup lang="ts">
503
+ import { ref, onMounted } from 'vue';
504
+ import type { ChatMessage, ChatRecord, BubbleEvent } from '@kernelift/ai-chat';
505
+ import { ElMessage } from 'element-plus';
506
+
507
+ // 响应式数据
508
+ const messages = ref<ChatMessage[]>([]);
509
+ const inputText = ref('');
510
+ const loading = ref(false);
511
+ const records = ref<ChatRecord[]>([]);
512
+ const brandColor = ref('#615ced');
513
+
514
+ // 初始化加载历史记录
515
+ onMounted(async () => {
516
+ try {
517
+ const history = await loadChatHistory();
518
+ records.value = history;
519
+ } catch (error) {
520
+ console.error('加载历史记录失败:', error);
521
+ }
522
+ });
523
+
524
+ // 发送消息处理
525
+ const handleSend = async (text: string) => {
526
+ if (!text.trim()) return;
527
+
528
+ // 添加用户消息
529
+ const userMessage: ChatMessage = {
530
+ id: generateId(),
531
+ role: 'user',
532
+ content: text,
533
+ timestamp: Date.now()
534
+ };
535
+
536
+ messages.value.push(userMessage);
537
+ inputText.value = '';
538
+ loading.value = true;
539
+
540
+ try {
541
+ // 调用AI接口
542
+ await callAIChatAPI(text, messages.value);
543
+ } catch (error) {
544
+ ElMessage.error('发送失败,请重试');
545
+ console.error('发送消息错误:', error);
546
+ } finally {
547
+ loading.value = false;
548
+ }
549
+ };
550
+
551
+ // 气泡事件处理
552
+ const handleBubbleEvent = (eventName: BubbleEvent, message: ChatMessage) => {
553
+ switch (eventName) {
554
+ case 'like':
555
+ handleFeedback(message, 'positive');
556
+ break;
557
+ case 'dislike':
558
+ handleFeedback(message, 'negative');
559
+ break;
560
+ case 'copy':
561
+ copyToClipboard(message.content);
562
+ ElMessage.success('已复制到剪贴板');
563
+ break;
564
+ case 'reload':
565
+ handleRegenerate(message);
566
+ break;
567
+ default:
568
+ console.log('未处理的事件:', eventName);
569
+ }
570
+ };
571
+
572
+ // 工具函数
573
+ const generateId = () => {
574
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
575
+ };
576
+
577
+ const copyToClipboard = async (text: string) => {
578
+ try {
579
+ await navigator.clipboard.writeText(text);
580
+ } catch (err) {
581
+ // 降级方案
582
+ const textArea = document.createElement('textarea');
583
+ textArea.value = text;
584
+ document.body.appendChild(textArea);
585
+ textArea.select();
586
+ document.execCommand('copy');
587
+ document.body.removeChild(textArea);
588
+ }
589
+ };
590
+ </script>
591
+
592
+ <style scoped>
593
+ .chat-app {
594
+ height: 100vh;
595
+ width: 100vw;
596
+ }
597
+
598
+ .custom-header {
599
+ display: flex;
600
+ justify-content: space-between;
601
+ align-items: center;
602
+ padding: 0 20px;
603
+ }
604
+
605
+ .sender-tools {
606
+ display: flex;
607
+ gap: 8px;
608
+ padding: 0 12px;
609
+ }
610
+ </style>
611
+ ```
612
+
613
+ ### 移动端适配示例
614
+
615
+ ```vue
616
+ <template>
617
+ <ChatContainer
618
+ v-model:messages="messages"
619
+ v-model:inputText="inputText"
620
+ :records="records"
621
+ :has-header="true"
622
+ :has-sender-tools="false"
623
+ :show-workspace="false"
624
+ @send="handleSend"
625
+ >
626
+ <!-- 移动端优化的发送器 -->
627
+ <template #sender-footer-tools="{ value, loading }">
628
+ <div class="mobile-tools">
629
+ <el-button :disabled="!value || loading" size="small" @click="handleQuickAction">
630
+ 快捷操作
631
+ </el-button>
632
+ </div>
633
+ </template>
634
+ </ChatContainer>
635
+ </template>
636
+
637
+ <style scoped>
638
+ /* 移动端样式优化 */
639
+ @media (max-width: 768px) {
640
+ :deep(.kernelift-chat-container) {
641
+ padding: 4px;
642
+ }
643
+
644
+ :deep(.kernelift-chat-sender) {
645
+ height: 160px;
646
+ }
647
+
648
+ :deep(.kernelift-chat-bubble__actions) {
649
+ opacity: 1; /* 移动端始终显示操作按钮 */
650
+ }
651
+ }
652
+ </style>
653
+ ```
654
+
655
+ ## API 参考
656
+
657
+ ### 主容器 Props
658
+
659
+ | 参数 | 类型 | 默认值 | 说明 |
660
+ | ------------------- | ------------------- | ----------- | ----------------------- |
661
+ | `messages` | `ChatMessage[]` | `[]` | **必需**,消息列表 |
662
+ | `inputText` | `string` | `''` | 输入框文本 |
663
+ | `loading` | `boolean` | `false` | 加载状态 |
664
+ | `records` | `ChatRecord[]` | `[]` | 聊天记录列表 |
665
+ | `hasHeader` | `boolean` | `true` | 是否显示头部 |
666
+ | `hasThemeMode` | `boolean` | `false` | 是否支持主题切换 |
667
+ | `hasSenderTools` | `boolean` | `false` | 是否显示发送工具 |
668
+ | `showWorkspace` | `boolean` | `true` | 是否显示工作区 |
669
+ | `themeMode` | `'light' \| 'dark'` | `'light'` | 主题模式 |
670
+ | `primaryColor` | `string` | `'#615ced'` | 主题色 |
671
+ | `defaultRecordId` | `string` | - | 默认选中的记录ID |
672
+ | `defaultCollapse` | `boolean` | `false` | 默认侧边栏折叠状态 |
673
+ | `defaultAsideWidth` | `number` | `250` | 侧边栏默认宽度 |
674
+ | `markdownClassName` | `string` | - | Markdown 内容自定义类名 |
675
+
676
+ ### 事件列表
677
+
678
+ | 事件名 | 参数 | 说明 |
679
+ | -------------------- | -------------------------- | ------------------------ |
680
+ | `send` | `string` | 发送消息时触发 |
681
+ | `change-record` | `ChatRecord \| undefined` | 切换聊天记录时触发 |
682
+ | `change-collapse` | `boolean` | 侧边栏折叠状态改变时触发 |
683
+ | `change-theme` | `'light' \| 'dark'` | 主题切换时触发 |
684
+ | `change-aside-width` | `number` | 侧边栏宽度改变时触发 |
685
+ | `click-logo` | - | 点击Logo时触发 |
686
+ | `bubble-event` | `BubbleEvent, ChatMessage` | 消息气泡交互事件 |
687
+ | `close-workspace` | - | 关闭工作区时触发 |
688
+
689
+ ### 插槽列表
690
+
691
+ | 插槽名 | 作用域 | 说明 |
692
+ | ------------------------ | ------------------------------------- | ---------------- |
693
+ | `aside` | `{ record: ChatRecord }` | 自定义侧边栏内容 |
694
+ | `header` | `{ record: ChatRecord }` | 自定义头部内容 |
695
+ | `logo` | - | 自定义Logo |
696
+ | `header-logo` | - | 折叠状态下的Logo |
697
+ | `bubble-header` | `{ data: ChatMessage }` | 消息气泡头部 |
698
+ | `bubble-footer` | `{ data: ChatMessage }` | 消息气泡底部 |
699
+ | `bubble-event` | `{ data: ChatMessage }` | 消息气泡操作区域 |
700
+ | `bubble-content-footer` | `{ data: ChatMessage }` | 消息内容底部 |
701
+ | `bubble-thinking-header` | `{ data: ChatMessage }` | 思考过程头部 |
702
+ | `sender-tools` | - | 发送器工具区域 |
703
+ | `sender-footer-tools` | `{ value: string, loading: boolean }` | 发送器底部工具 |
704
+ | `footer` | - | 页面底部内容 |
705
+ | `workspace` | `{ record: ChatRecord }` | 工作区内容 |
706
+
707
+ ---
708
+
709
+ **版本**: 1.0.0
710
+ **兼容性**: Vue 3.3+
711
+ **License**: 禁止商业使用