@blueking/chat-x 0.0.50 → 0.0.51-beta.2

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.
Files changed (147) hide show
  1. package/dist/ag-ui/types/contents.d.ts +2 -0
  2. package/dist/ag-ui/types/messages.d.ts +5 -0
  3. package/dist/common/constants.d.ts +1 -1
  4. package/dist/components/ai-buttons/file-upload-btn/file-upload-btn.vue.d.ts +0 -2
  5. package/dist/components/chat-content/file-content/file-content.vue.d.ts +5 -2
  6. package/dist/components/chat-content/file-content/upload-file-item.vue.d.ts +12 -0
  7. package/dist/components/chat-content/file-content/upload-image-item.vue.d.ts +21 -0
  8. package/dist/components/chat-input/ai-slash-editor/ai-slash-editor.vue.d.ts +1 -1
  9. package/dist/components/chat-input/build-default-placeholder.d.ts +7 -0
  10. package/dist/components/chat-input/chat-input.vue.d.ts +1 -1
  11. package/dist/components/chat-message/message-container/message-container.vue.d.ts +1 -0
  12. package/dist/components/chat-message/message-render/message-render.vue.d.ts +2 -0
  13. package/dist/components/chat-message/user-message/user-message.vue.d.ts +3 -1
  14. package/dist/components/index.d.ts +2 -1
  15. package/dist/components/message-tools/message-time/format-message-time.d.ts +8 -0
  16. package/dist/components/message-tools/message-time/message-time.vue.d.ts +8 -0
  17. package/dist/components/message-tools/message-tools.vue.d.ts +11 -1
  18. package/dist/composables/use-custom-tab.d.ts +5 -3
  19. package/dist/composables/use-global-config.d.ts +3 -0
  20. package/dist/composables/use-message-group.d.ts +150 -72
  21. package/dist/icons/execution.d.ts +6 -0
  22. package/dist/icons/tools.d.ts +3 -0
  23. package/dist/index.css +1 -1
  24. package/dist/index.js +3124 -2837
  25. package/dist/index.js.map +1 -1
  26. package/dist/lang/lang.d.ts +8 -7
  27. package/dist/mcp/generated/docs/ai-slash-input.md +2 -0
  28. package/dist/mcp/generated/docs/assistant-message.md +9 -7
  29. package/dist/mcp/generated/docs/chat-container.md +38 -32
  30. package/dist/mcp/generated/docs/chat-input.md +18 -12
  31. package/dist/mcp/generated/docs/cite-content.md +3 -3
  32. package/dist/mcp/generated/docs/desc-panel.md +32 -10
  33. package/dist/mcp/generated/docs/execution-summary.md +3 -3
  34. package/dist/mcp/generated/docs/file-artifact-panel.md +6 -4
  35. package/dist/mcp/generated/docs/file-content.md +89 -73
  36. package/dist/mcp/generated/docs/file-upload-btn.md +16 -18
  37. package/dist/mcp/generated/docs/message-container.md +3 -0
  38. package/dist/mcp/generated/docs/message-render.md +2 -1
  39. package/dist/mcp/generated/docs/message-time.md +180 -0
  40. package/dist/mcp/generated/docs/message-tools.md +47 -12
  41. package/dist/mcp/generated/docs/messages.md +9 -0
  42. package/dist/mcp/generated/docs/toolcall-render.md +82 -43
  43. package/dist/mcp/generated/docs/use-artifact-preview.md +19 -17
  44. package/dist/mcp/generated/docs/use-custom-tab.md +12 -8
  45. package/dist/mcp/generated/docs/use-global-config.md +15 -5
  46. package/dist/mcp/generated/docs/user-message.md +9 -0
  47. package/dist/mcp/generated/docs/user-question-card.md +2 -0
  48. package/dist/mcp/generated/index.json +46 -6
  49. package/dist/types/input.d.ts +6 -0
  50. package/dist/utils/file.d.ts +7 -1
  51. package/dist/utils/index.d.ts +2 -0
  52. package/dist/utils/merge-tools-by-id.d.ts +6 -0
  53. package/dist/utils/upload-file.d.ts +35 -0
  54. package/package.json +3 -2
  55. package/skills/blueking-chat-x/SKILL.md +139 -0
  56. package/skills/blueking-chat-x/references/_index.md +131 -0
  57. package/skills/blueking-chat-x/references/components/activity-layout.md +134 -0
  58. package/skills/blueking-chat-x/references/components/activity-message.md +486 -0
  59. package/skills/blueking-chat-x/references/components/ai-image.md +230 -0
  60. package/skills/blueking-chat-x/references/components/ai-loading.md +131 -0
  61. package/skills/blueking-chat-x/references/components/ai-prompt-list.md +44 -0
  62. package/skills/blueking-chat-x/references/components/ai-selection.md +439 -0
  63. package/skills/blueking-chat-x/references/components/ai-skill-list.md +73 -0
  64. package/skills/blueking-chat-x/references/components/ai-slash-editor.md +43 -0
  65. package/skills/blueking-chat-x/references/components/ai-slash-input.md +56 -0
  66. package/skills/blueking-chat-x/references/components/ai-slash-menu.md +42 -0
  67. package/skills/blueking-chat-x/references/components/animation-text.md +202 -0
  68. package/skills/blueking-chat-x/references/components/assistant-message.md +498 -0
  69. package/skills/blueking-chat-x/references/components/chat-container.md +869 -0
  70. package/skills/blueking-chat-x/references/components/chat-input.md +769 -0
  71. package/skills/blueking-chat-x/references/components/cite-content.md +142 -0
  72. package/skills/blueking-chat-x/references/components/code-content.md +211 -0
  73. package/skills/blueking-chat-x/references/components/common-error-content.md +73 -0
  74. package/skills/blueking-chat-x/references/components/content-render.md +233 -0
  75. package/skills/blueking-chat-x/references/components/delete-tool.md +191 -0
  76. package/skills/blueking-chat-x/references/components/desc-panel.md +162 -0
  77. package/skills/blueking-chat-x/references/components/detail-section.md +91 -0
  78. package/skills/blueking-chat-x/references/components/execution-summary.md +128 -0
  79. package/skills/blueking-chat-x/references/components/file-artifact-panel.md +289 -0
  80. package/skills/blueking-chat-x/references/components/file-content.md +319 -0
  81. package/skills/blueking-chat-x/references/components/file-icon.md +109 -0
  82. package/skills/blueking-chat-x/references/components/file-upload-btn.md +159 -0
  83. package/skills/blueking-chat-x/references/components/flow-agent-content.md +264 -0
  84. package/skills/blueking-chat-x/references/components/flow-agent-node-detail.md +236 -0
  85. package/skills/blueking-chat-x/references/components/highlight-keyword.md +146 -0
  86. package/skills/blueking-chat-x/references/components/image-content.md +182 -0
  87. package/skills/blueking-chat-x/references/components/image-preview-group.md +184 -0
  88. package/skills/blueking-chat-x/references/components/image-preview.md +226 -0
  89. package/skills/blueking-chat-x/references/components/info-message.md +144 -0
  90. package/skills/blueking-chat-x/references/components/input-attachment.md +49 -0
  91. package/skills/blueking-chat-x/references/components/input-info-alert.md +42 -0
  92. package/skills/blueking-chat-x/references/components/interrupt-message.md +212 -0
  93. package/skills/blueking-chat-x/references/components/key-value-content.md +128 -0
  94. package/skills/blueking-chat-x/references/components/knowledge-rag-content.md +122 -0
  95. package/skills/blueking-chat-x/references/components/latex-content.md +200 -0
  96. package/skills/blueking-chat-x/references/components/loading-message.md +192 -0
  97. package/skills/blueking-chat-x/references/components/markdown-content.md +232 -0
  98. package/skills/blueking-chat-x/references/components/mermaid-content.md +189 -0
  99. package/skills/blueking-chat-x/references/components/message-container.md +645 -0
  100. package/skills/blueking-chat-x/references/components/message-loading.md +118 -0
  101. package/skills/blueking-chat-x/references/components/message-render.md +327 -0
  102. package/skills/blueking-chat-x/references/components/message-time.md +177 -0
  103. package/skills/blueking-chat-x/references/components/message-tools.md +416 -0
  104. package/skills/blueking-chat-x/references/components/model-selector.md +155 -0
  105. package/skills/blueking-chat-x/references/components/preview-toolbar.md +42 -0
  106. package/skills/blueking-chat-x/references/components/questions-container.md +85 -0
  107. package/skills/blueking-chat-x/references/components/reasoning-message.md +232 -0
  108. package/skills/blueking-chat-x/references/components/reference-content.md +135 -0
  109. package/skills/blueking-chat-x/references/components/reference-doc-content.md +109 -0
  110. package/skills/blueking-chat-x/references/components/scroll-btn.md +159 -0
  111. package/skills/blueking-chat-x/references/components/selection-footer.md +78 -0
  112. package/skills/blueking-chat-x/references/components/selection-question.md +88 -0
  113. package/skills/blueking-chat-x/references/components/shortcut-btn.md +204 -0
  114. package/skills/blueking-chat-x/references/components/shortcut-btns.md +266 -0
  115. package/skills/blueking-chat-x/references/components/shortcut-render.md +424 -0
  116. package/skills/blueking-chat-x/references/components/simple-table.md +101 -0
  117. package/skills/blueking-chat-x/references/components/text-content.md +77 -0
  118. package/skills/blueking-chat-x/references/components/tool-approval-card.md +183 -0
  119. package/skills/blueking-chat-x/references/components/tool-btn.md +317 -0
  120. package/skills/blueking-chat-x/references/components/tool-message.md +235 -0
  121. package/skills/blueking-chat-x/references/components/toolcall-render.md +348 -0
  122. package/skills/blueking-chat-x/references/components/user-feedback.md +233 -0
  123. package/skills/blueking-chat-x/references/components/user-message.md +424 -0
  124. package/skills/blueking-chat-x/references/components/user-question-answered-card.md +104 -0
  125. package/skills/blueking-chat-x/references/components/user-question-card.md +231 -0
  126. package/skills/blueking-chat-x/references/components/user-question-choice.md +105 -0
  127. package/skills/blueking-chat-x/references/components/user-question-option.md +42 -0
  128. package/skills/blueking-chat-x/references/components/vnode-renderer.md +122 -0
  129. package/skills/blueking-chat-x/references/composables/use-animation-text.md +196 -0
  130. package/skills/blueking-chat-x/references/composables/use-artifact-preview.md +231 -0
  131. package/skills/blueking-chat-x/references/composables/use-clipboard.md +203 -0
  132. package/skills/blueking-chat-x/references/composables/use-command-selection.md +150 -0
  133. package/skills/blueking-chat-x/references/composables/use-container-scroll.md +57 -0
  134. package/skills/blueking-chat-x/references/composables/use-custom-tab.md +158 -0
  135. package/skills/blueking-chat-x/references/composables/use-flow-node-actions.md +157 -0
  136. package/skills/blueking-chat-x/references/composables/use-full-screen.md +112 -0
  137. package/skills/blueking-chat-x/references/composables/use-global-config.md +148 -0
  138. package/skills/blueking-chat-x/references/composables/use-menu-keydown.md +163 -0
  139. package/skills/blueking-chat-x/references/composables/use-message-group.md +247 -0
  140. package/skills/blueking-chat-x/references/composables/use-observer-visible-list.md +188 -0
  141. package/skills/blueking-chat-x/references/composables/use-parent-scrolling.md +46 -0
  142. package/skills/blueking-chat-x/references/theme/theme.md +431 -0
  143. package/skills/blueking-chat-x/references/types/constants.md +307 -0
  144. package/skills/blueking-chat-x/references/types/interrupt.md +379 -0
  145. package/skills/blueking-chat-x/references/types/messages.md +553 -0
  146. package/skills/blueking-chat-x/references/types/schema.md +91 -0
  147. package/skills/blueking-chat-x/scripts/generate-references.mjs +314 -0
@@ -0,0 +1,769 @@
1
+ # ChatInput 聊天输入框
2
+
3
+ > 能力域:输入交互 | 导入:`import { ChatInput } from '@blueking/chat-x'` | since 1.0.0
4
+
5
+ 聊天输入区,组合富文本输入、快捷指令、附件、引用、发送/停止等交互。 源码位置:src/components/chat-input/chat-input.vue。
6
+
7
+ **关联**:shortcut-btns(底部附件区默认展示的快捷指令列表)、shortcut-btn(已选快捷指令以单按钮形式展示并可关闭)、shortcut-render(快捷指令含 components 时由外层唤起表单渲染)、chat-container(顶层聊天布局中作为输入区子组件)、model-selector(传入 models 后在发送按钮左侧默认展示模型选择器)、cite-content(消息引用区展示选中的上下文片段)
8
+
9
+ ---
10
+
11
+ # ChatInput 聊天输入框
12
+ ## 源码事实
13
+
14
+ - **源码位置**:`src/components/chat-input/chat-input.vue`
15
+ - **能力域**:输入交互
16
+ - **能力说明**:聊天输入区,组合富文本输入、快捷指令、附件、引用、发送/停止等交互。
17
+
18
+ > **能力域**:输入交互
19
+
20
+ 聊天消息输入框组件,支持快捷指令选择、资源 `@` 引用、Prompt `/` 模板、消息引用、文件上传(拖拽/粘贴/点击)等功能。
21
+
22
+ ## 组件结构
23
+
24
+ ```
25
+ ai-chat-input-container(padding: 0 16px 16px,底部间距 16px)
26
+ ├── slot#top(容器顶部,在输入框框体外侧)
27
+ ├── slot#interrupt(容器顶部,在输入框框体外侧,通常展示中断/审批提示)
28
+ └── chat-input(框体,受 inputMaxHeight 控制)
29
+ ├── slot#input-header(默认:cite 不为空时渲染引用区)
30
+ ├── slot#files(默认:有上传文件时渲染文件预览区)
31
+ ├── AiSlashInput(富文本编辑器,/ 触发 Prompt,@ 触发资源)
32
+ └── InputAttachment(底部工具栏,高度固定 40px)
33
+ ├── FileUploadBtn(仅当 supportUpload 为 true 时显示,在 slot#attachment 外部)
34
+ ├── 分隔线(仅当 supportUpload 为 true 且有快捷指令时显示)
35
+ ├── slot#attachment(默认:ShortcutBtns 或已选 ShortcutBtn + 关闭图标)
36
+ ├── slot#before-send(默认:传入 models 时渲染 ModelSelector)
37
+ └── slot#send-icon(默认:发送/停止图标,仅替换图标,按钮容器保留)
38
+ ```
39
+
40
+ > **注意**:`slot#attachment` 只替换快捷指令区,`FileUploadBtn` 在其外部,使用该 slot 不会移除上传按钮。`slot#send-icon` 只替换图标,按钮的点击处理和样式仍由组件控制。
41
+
42
+ ### AiSlashInput 与 modelValue 同步
43
+
44
+ 内部编辑器 `AiSlashInput` 在 **`modelValue` 由外部异步更新**(例如从历史会话回填、父组件重置)且与当前文档不一致时,会通过 `useCommandSelection` 提供的 `GetDocSnapshot` 读取编辑器快照,与 `docToString(modelValue)` 比对后,必要时执行 `ReplaceAll` 将编辑器内容同步为新的 `modelValue`,避免内外状态脱节。
45
+
46
+ ## 基础用法
47
+
48
+ ```vue
49
+ <template>
50
+ <ChatInput
51
+ v-model="inputValue"
52
+ :message-status="messageStatus"
53
+ :on-send-message="handleSendMessage"
54
+ :on-stop-sending="handleStopSending"
55
+ />
56
+ </template>
57
+
58
+ <script setup lang="ts">
59
+ import { ref } from 'vue';
60
+ import { ChatInput, MessageStatus, type TagSchema } from '@blueking/chat-x';
61
+
62
+ const inputValue = ref('');
63
+ const messageStatus = ref(MessageStatus.Complete);
64
+
65
+ const handleSendMessage = async (content: string, docSchema: TagSchema) => {
66
+ // content:纯文本字符串(无文件时)或 InputContent[] 数组(有文件时)
67
+ messageStatus.value = MessageStatus.Streaming;
68
+ // ... 发送 AI 请求
69
+ messageStatus.value = MessageStatus.Complete;
70
+ };
71
+
72
+ const handleStopSending = async () => {
73
+ messageStatus.value = MessageStatus.Stop;
74
+ };
75
+ </script>
76
+ ```
77
+
78
+ **渲染效果**(输入 `/` 唤出 Prompt,输入 `@` 唤出资源菜单)
79
+
80
+ ## 发送状态(messageStatus)
81
+
82
+ `messageStatus` 控制底部工具栏的按钮渲染,但**输入框为空且没有附件时始终自动置灰禁用**,无论 `messageStatus` 传入什么值。
83
+
84
+ | `messageStatus` | 输入框有内容或已有附件时按钮表现 | 输入框空且无附件时 |
85
+ | ----------------------------- | ------------------------------------------------------ | ------------------------ |
86
+ | `complete` / `stop` / `error` | 蓝色发送按钮,点击触发 `onSendMessage` | 灰色禁用 |
87
+ | `streaming` / `pending` / `fetching` | 蓝色停止按钮(Loading 图标),点击触发 `onStopSending` | 蓝色停止按钮(仍可点击) |
88
+ | `disabled` | 灰色禁用,点击无效 | 灰色禁用 |
89
+
90
+ > **实现细节**:组件内部用 `messageState` 计算属性决定实际按钮状态:当 `messageStatus` 为 `pending`、`streaming` 或 `fetching` 时直接使用该状态(确保停止按钮始终可用);否则**已有上传附件时视为可发送**(纯附件消息无需输入文字);再否则当输入为空或仅含空白字符时强制为 `disabled`,其余情况使用 `messageStatus` 的值。`fetching` 时按 Enter **不会**触发发送(避免请求中与 Loading 占位阶段重复提交)。
91
+
92
+ > **纯附件消息**:只上传附件不输入文字时,`onSendMessage` 的 `content` 只含 `binary` 项,**不会**附带空文本段。
93
+
94
+ ### onSendMessage 第三参数 options(UserQuestion 上下文)
95
+
96
+ `ChatInput` 自身调用 `onSendMessage` 时只传前两个参数。当组件被 [ChatContainer](/components/setup/chat-container) 包裹且存在待回答 `UserQuestion` 中断时,容器会在用户点击发送时注入第三个参数:
97
+
98
+ | 字段 | 类型 | 说明 |
99
+ | ----------- | ------------------ | -------------------------------------------------------------------- |
100
+ | `interrupt` | `Interrupt` | 当前激活的 `UserQuestionInterrupt` |
101
+ | `payload` | `InterruptResume` | skip resume(`status: 'cancelled'`,`payload.answers` 为空数组) |
102
+
103
+ 此场景下容器**不会**自动清空 `modelValue`,业务侧需在 `onSendMessage` 内自行处理消息发送与 `resumeAgent` 的先后顺序。结构化作答仍通过 `UserQuestionCard` → `onInterruptResume` 完成。
104
+
105
+ ```typescript
106
+ const handleSendMessage = async (
107
+ content: UserMessage['content'],
108
+ docSchema: TagSchema,
109
+ options?: { interrupt?: Interrupt; payload?: InterruptResume },
110
+ ) => {
111
+ if (options?.interrupt && options?.payload) {
112
+ await resumeAgent({ interruptId: options.interrupt.id, resume: options.payload });
113
+ return;
114
+ }
115
+ await sendMessage(content, docSchema);
116
+ };
117
+ ```
118
+
119
+ ### sendDisabledTip(业务阻塞发送)
120
+
121
+ 当业务侧需要临时阻止发送但仍允许用户输入时,可传入 `sendDisabledTip`。组件会置灰发送按钮,按钮 tooltip 展示该文案,并拦截点击发送、按 Enter 发送和 `triggerSendMessage()`:
122
+
123
+ ```vue
124
+ <template>
125
+ <ChatInput
126
+ v-model="inputValue"
127
+ message-status="complete"
128
+ :send-disabled-tip="interruptTip"
129
+ :on-send-message="handleSendMessage"
130
+ :on-stop-sending="handleStopSending"
131
+ >
132
+ <template #interrupt>
133
+ <div class="input-alert">{{ interruptTip }}</div>
134
+ </template>
135
+ </ChatInput>
136
+ </template>
137
+ ```
138
+
139
+ **渲染效果**
140
+
141
+ ### Complete(可发送)
142
+
143
+ ### Streaming(流式输出中,显示停止按钮)
144
+
145
+ ### Disabled(禁用)
146
+
147
+ ## 引用消息(v-model:cite)
148
+
149
+ 通过 `v-model:cite` 绑定引用内容,引用区域显示在编辑器上方,用户可点击关闭按钮取消引用。发送时通过 `onSendMessage` 的第一个参数获取输入内容,引用内容需自行通过 `cite` 变量读取:
150
+
151
+ ```vue
152
+ <template>
153
+ <ChatInput
154
+ v-model="inputValue"
155
+ v-model:cite="citeContent"
156
+ :message-status="messageStatus"
157
+ :on-send-message="handleSendMessage"
158
+ :on-stop-sending="handleStopSending"
159
+ />
160
+ </template>
161
+
162
+ <script setup lang="ts">
163
+ import { ref } from 'vue';
164
+ import { ChatInput, MessageStatus, type TagSchema } from '@blueking/chat-x';
165
+
166
+ const inputValue = ref('');
167
+ const citeContent = ref('被引用的消息内容...');
168
+ const messageStatus = ref(MessageStatus.Complete);
169
+
170
+ const handleSendMessage = async (content: string, docSchema: TagSchema) => {
171
+ console.log('输入内容:', content);
172
+ console.log('引用内容:', citeContent.value); // 自行读取引用内容
173
+ citeContent.value = ''; // 发送后清空引用
174
+ };
175
+
176
+ const handleStopSending = async () => {
177
+ messageStatus.value = MessageStatus.Stop;
178
+ };
179
+ </script>
180
+ ```
181
+
182
+ **渲染效果**(顶部引用区,点击右侧 × 关闭引用)
183
+
184
+ ## Skill 列表(`/` 触发)
185
+
186
+ 通过 `skills` 传入 Skill 列表,用户在编辑器中输入 `/` 唤出 [AiSkillList](/components/input/ai-skill-list) 菜单,支持按名称/编码模糊搜索,选择后以 Skill 标签嵌入编辑器。无 icon 或 icon 加载失败时展示首字母 fallback。已插入的 Skill 不会再出现在下拉菜单中(自动去重)。
187
+
188
+ ```vue
189
+ <script setup lang="ts">
190
+ import type { ISkillListItem } from '@blueking/chat-x';
191
+
192
+ const skills: ISkillListItem[] = [
193
+ { skill_code: 'translate', skill_name: '翻译', description: '翻译文本', icon: '' },
194
+ { skill_code: 'summarize', skill_name: '总结', description: '总结内容', icon: 'https://example.com/icon.png' },
195
+ ];
196
+ </script>
197
+ ```
198
+
199
+ ## Prompt 模板(`\` 触发)
200
+
201
+ 通过 `prompts` 传入字符串数组,用户在编辑器中输入 `\` 唤出 Prompt 菜单,支持模糊搜索,选择后自动填入编辑器:
202
+
203
+ ```vue
204
+ <script setup lang="ts">
205
+ const prompts = [
206
+ '帮我写一篇关于 {topic} 的文章',
207
+ '解释一下这段代码的作用',
208
+ '请用简洁的语言总结以下内容',
209
+ '将以下内容翻译成英文',
210
+ '帮我优化这段代码',
211
+ ];
212
+ </script>
213
+ ```
214
+
215
+ ## 资源 `@` 引用(`@` 触发)
216
+
217
+ 通过 `resources` 传入资源列表,用户输入 `@` 唤出资源选择菜单。资源按 `type` 分组展示,选中后以 Tag 标签形式嵌入编辑器。已选中的资源不会再出现在下拉菜单中(自动去重)。
218
+
219
+ 通过监听 `@update:model-value` 事件的第二个参数 `selectedResourceList` 可以获取当前编辑器中已选中的资源列表:
220
+
221
+ ```vue
222
+ <template>
223
+ <ChatInput
224
+ :model-value="inputValue"
225
+ :message-status="messageStatus"
226
+ :prompts="prompts"
227
+ :resources="resources"
228
+ :on-send-message="handleSendMessage"
229
+ :on-stop-sending="handleStopSending"
230
+ @update:model-value="handleModelValueUpdate"
231
+ />
232
+ </template>
233
+
234
+ <script setup lang="ts">
235
+ import { ref } from 'vue';
236
+ import { ChatInput, MessageStatus, type TagSchema, type IAiSlashMenuItem } from '@blueking/chat-x';
237
+
238
+ const inputValue = ref('');
239
+ const messageStatus = ref(MessageStatus.Complete);
240
+
241
+ const resources: IAiSlashMenuItem[] = [
242
+ { id: 'tool1', name: '天气查询', type: 'tool', icon: 'icon-tool' },
243
+ { id: 'tool2', name: '代码执行', type: 'tool', icon: 'icon-tool' },
244
+ { id: 'mcp1', name: 'db-server', type: 'mcp', icon: 'icon-mcp' },
245
+ { id: 'doc1', name: 'API 文档', type: 'doc', icon: 'icon-doc' },
246
+ { id: 'sc1', name: '翻译助手', type: 'shortcut', icon: 'icon-shortcut' },
247
+ ];
248
+
249
+ const handleModelValueUpdate = (value: string | TagSchema, selectedResourceList: IAiSlashMenuItem[]) => {
250
+ inputValue.value = value;
251
+ // selectedResourceList 为当前编辑器中已选中的 @ 资源列表
252
+ console.log('已选资源:', selectedResourceList);
253
+ };
254
+
255
+ const handleSendMessage = async (content: string, docSchema: TagSchema) => {
256
+ console.log('发送内容(含 @ 引用):', docSchema);
257
+ };
258
+
259
+ const handleStopSending = async () => {
260
+ messageStatus.value = MessageStatus.Stop;
261
+ };
262
+ </script>
263
+ ```
264
+
265
+ > 基础用法 demo 已集成 Prompt 和资源功能,可在上方输入框中体验 `/` 和 `@` 操作。
266
+ >
267
+ > **注意**:`v-model` 仍可使用(Vue 自动取第一个参数绑定),但如需获取 `selectedResourceList`,应使用 `@update:model-value` 显式监听。
268
+
269
+ ## 快捷指令
270
+
271
+ 通过 `shortcuts` 传入快捷指令列表,底部工具栏展示快捷指令按钮;通过 `shortcutId` + `shortcuts` 配合控制选中状态:
272
+
273
+ - `shortcutId` 为空 → 显示所有快捷指令按钮列表
274
+ - `shortcutId` 匹配某个 `shortcut.id` → 隐藏列表,显示已选指令 + 关闭图标
275
+
276
+ ```vue
277
+ <template>
278
+ <ChatInput
279
+ v-model="inputValue"
280
+ :message-status="messageStatus"
281
+ :shortcuts="shortcuts"
282
+ :shortcut-id="selectedShortcutId"
283
+ :on-send-message="handleSendMessage"
284
+ :on-stop-sending="handleStopSending"
285
+ @select-shortcut="selectedShortcutId = $event.id"
286
+ @delete-shortcut="selectedShortcutId = ''"
287
+ />
288
+ </template>
289
+
290
+ <script setup lang="ts">
291
+ import { ref } from 'vue';
292
+ import { ChatInput, MessageStatus, type TagSchema, type Shortcut } from '@blueking/chat-x';
293
+
294
+ const inputValue = ref('');
295
+ const messageStatus = ref(MessageStatus.Complete);
296
+ const selectedShortcutId = ref('');
297
+
298
+ const shortcuts: Shortcut[] = [
299
+ { id: 'translate', name: '翻译' },
300
+ { id: 'explain', name: '解释代码' },
301
+ { id: 'summarize', name: '总结' },
302
+ ];
303
+
304
+ const handleSendMessage = async (content: string, docSchema: TagSchema) => {
305
+ console.log('发送内容:', content, '当前快捷指令:', selectedShortcutId.value);
306
+ };
307
+
308
+ const handleStopSending = async () => {
309
+ messageStatus.value = MessageStatus.Stop;
310
+ };
311
+ </script>
312
+ ```
313
+
314
+ **渲染效果**(底部显示快捷指令按钮,点击选中后按钮变为已选状态)
315
+
316
+ ## 文件上传 {#file-upload}
317
+
318
+ `supportUpload` 默认为 `true`,底部工具栏自动显示文件上传按钮。传入 `onUpload` 回调后即可处理文件上传:
319
+
320
+ - 底部工具栏出现文件上传按钮(在快捷指令左侧)
321
+ - 支持**点击选择**、**拖拽上传**、**粘贴上传**(Ctrl+V)
322
+ - `onUpload` 每次传入**单个** `File`,返回 `{ download_url?: string }`
323
+ - 文件自动去重(基于 `name + size + lastModified` 复合键),不会重复上传
324
+ - 发送成功后,`uploadFiles` 自动清空
325
+
326
+ **个数与大小校验(与 `FileUploadBtn` 分工)**:
327
+
328
+ - 列表最多保留 **`MAX_UPLOAD_FILES`(9)** 个待发送附件;已满时再次选择/拖入/粘贴文件会弹出 **bkui-vue `Message` 错误提示**(`formatUploadNotAddedMessage`),且不会继续入队。
329
+ - 在未满的前提下:空文件、单文件大小 **`>= MAX_UPLOAD_FILE_SIZE`(约 2.4MB)**、或与已有文件重复的项会被跳过;若本轮有任意文件因此未加入列表,会在处理结束后弹出**同一条文案风格**的错误提示,汇总未成功添加的数量。
330
+ - `FileUploadBtn` 仅在按钮层过滤**空文件与单文件超大**,把合法文件以数组形式 `upload` 上来;**个数上限与重复校验**在 `ChatInput` 的 `handleUpload` 中统一处理,避免与按钮层各弹一条提示。
331
+
332
+ **发送内容格式**(有文件时):
333
+
334
+ ```typescript
335
+ // onSendMessage 的 content 参数变为数组:
336
+ [
337
+ { type: 'binary', url: '...', mimeType: 'image/png', filename: 'a.png' },
338
+ { type: 'binary', url: '...', mimeType: 'application/pdf', filename: 'b.pdf' },
339
+ // 若输入框也有文字,则最后追加:
340
+ { type: 'text', text: '请帮我分析这两个文件' },
341
+ ];
342
+ ```
343
+
344
+ ```vue
345
+ <template>
346
+ <ChatInput
347
+ v-model="inputValue"
348
+ :message-status="messageStatus"
349
+ :on-send-message="handleSendMessage"
350
+ :on-stop-sending="handleStopSending"
351
+ :on-upload="handleUpload"
352
+ />
353
+ </template>
354
+
355
+ <script setup lang="ts">
356
+ import { ref } from 'vue';
357
+ import { ChatInput, MessageStatus, type UserMessage, type TagSchema } from '@blueking/chat-x';
358
+
359
+ const inputValue = ref('');
360
+ const messageStatus = ref(MessageStatus.Complete);
361
+
362
+ const handleSendMessage = async (content: UserMessage['content'], docSchema: TagSchema) => {
363
+ if (Array.isArray(content)) {
364
+ // 有文件时 content 为数组
365
+ content.forEach(item => {
366
+ if (item.type === 'binary') console.log('文件:', item.filename, item.url);
367
+ if (item.type === 'text') console.log('文字:', item.text);
368
+ });
369
+ } else {
370
+ // 无文件时 content 为纯字符串
371
+ console.log('文字:', content);
372
+ }
373
+ };
374
+
375
+ const handleStopSending = async () => {
376
+ messageStatus.value = MessageStatus.Stop;
377
+ };
378
+
379
+ // 每次传入单个 File,需返回 { download_url: string }
380
+ const handleUpload = async (file: File) => {
381
+ const formData = new FormData();
382
+ formData.append('file', file);
383
+ const res = await fetch('/api/upload', { method: 'POST', body: formData });
384
+ return res.json(); // { download_url: '...' }
385
+ };
386
+ </script>
387
+ ```
388
+
389
+ **渲染效果**(底部出现文件上传按钮,支持点击、拖拽、粘贴上传)
390
+
391
+ ## 预设上传文件(defaultUploadFiles)
392
+
393
+ 通过 `defaultUploadFiles` 设置初始已上传的文件列表,文件出现在文件预览区,随下次发送一起携带:
394
+
395
+ ```vue
396
+ <script setup lang="ts">
397
+ import { type UploadFile, UploadStatus } from '@blueking/chat-x';
398
+
399
+ const defaultFiles: UploadFile[] = [
400
+ {
401
+ type: 'binary',
402
+ url: 'https://example.com/report.pdf',
403
+ filename: 'report.pdf',
404
+ mimeType: 'application/pdf',
405
+ status: UploadStatus.Success,
406
+ },
407
+ ];
408
+ </script>
409
+ ```
410
+
411
+ ## 自定义占位符
412
+
413
+ 未传入 `placeholder` 时,组件会按 `skills` / `prompts` / `resources` 是否非空动态生成提示行。传入后完全覆盖,支持多行(换行用 `\n`):
414
+
415
+ ```vue
416
+ <template>
417
+ <ChatInput
418
+ v-model="inputValue"
419
+ :message-status="messageStatus"
420
+ :placeholder="placeholder"
421
+ :on-send-message="handleSendMessage"
422
+ :on-stop-sending="handleStopSending"
423
+ />
424
+ </template>
425
+
426
+ <script setup lang="ts">
427
+ // 多行占位符
428
+ const placeholder = `你好,我是 AI 小鲸!
429
+ 输入 "/" 唤出 Prompt
430
+ 输入 "@" 唤出工具
431
+ 按 Shift + Enter 换行`;
432
+ </script>
433
+ ```
434
+
435
+ **渲染效果**
436
+
437
+ ## 自定义插槽
438
+
439
+ 组件提供 6 个插槽用于自定义各区域内容:
440
+
441
+ | 插槽名 | 位置 | 默认行为 |
442
+ | -------------- | ------------------------------------------ | ------------------------------------------ |
443
+ | `top` | 框体外部顶部(`.chat-input` 上方) | 无 |
444
+ | `interrupt` | 框体外部顶部,位于 `top` 之后 | 无,通常用于展示审批/中断提示 |
445
+ | `input-header` | 框体内部顶部(编辑器上方) | `cite` 不为空时渲染引用区(`CiteContent`) |
446
+ | `files` | 文件预览区 | 有上传文件时渲染 `FileContent` |
447
+ | `attachment` | 底部工具栏快捷指令区(FileUploadBtn 右侧) | 快捷指令按钮列表 / 已选快捷指令 |
448
+ | `send-icon` | 发送按钮内的图标 | 发送图标 / 停止图标(按钮容器由组件控制) |
449
+
450
+ ```vue
451
+ <template>
452
+ <ChatInput
453
+ v-model="inputValue"
454
+ :message-status="messageStatus"
455
+ :on-send-message="handleSendMessage"
456
+ :on-stop-sending="handleStopSending"
457
+ >
458
+ <!-- 框体外顶部,适合展示模型信息、Token 消耗等 -->
459
+ <template #top>
460
+ <div class="input-tips">当前模型: GPT-4 · 剩余 Token: 12,800</div>
461
+ </template>
462
+
463
+ <!-- 框体外顶部,适合展示中断、审批等业务提示 -->
464
+ <template #interrupt>
465
+ <div class="input-alert">当前会话有待审批单,暂时不能继续发送</div>
466
+ </template>
467
+
468
+ <!-- 替换引用区,可自定义引用样式 -->
469
+ <template #input-header>
470
+ <div class="custom-header">自定义头部内容</div>
471
+ </template>
472
+
473
+ <!-- 替换文件预览区,接收 files 参数 -->
474
+ <template #files="{ files }">
475
+ <div class="custom-files">
476
+ <span
477
+ v-for="file in files"
478
+ :key="file.filename"
479
+ >{{ file.filename }}</span
480
+ >
481
+ </div>
482
+ </template>
483
+
484
+ <!-- 替换快捷指令区(FileUploadBtn 仍在左侧) -->
485
+ <template #attachment>
486
+ <button @click="handleCustomAction">🎯 自定义操作</button>
487
+ </template>
488
+
489
+ <!-- 替换发送按钮图标(点击逻辑不变) -->
490
+ <template #send-icon>
491
+ <span>🚀</span>
492
+ </template>
493
+ </ChatInput>
494
+ </template>
495
+ ```
496
+
497
+ **渲染效果**(顶部自定义模型信息与中断提示)
498
+
499
+ ## 模型选择
500
+
501
+ 传入 `models` 后,会在发送按钮左侧展示 [ModelSelector](/components/input/model-selector)。选中值(模型的 `llm_name`)通过 `v-model:selected-model` 双向绑定,`@model-change` 可获取完整模型对象。能力标签由组件依据 `property` 自动派生。
502
+
503
+ ```vue
504
+ <template>
505
+ <ChatInput
506
+ v-model="inputValue"
507
+ v-model:selected-model="selectedModel"
508
+ :message-status="messageStatus"
509
+ :models="models"
510
+ :on-send-message="handleSendMessage"
511
+ @model-change="handleModelChange"
512
+ />
513
+ </template>
514
+
515
+ <script setup lang="ts">
516
+ import { ref } from 'vue';
517
+ import { ChatInput, MessageStatus, type IModelOption, type TagSchema } from '@blueking/chat-x';
518
+
519
+ const inputValue = ref('');
520
+ // 选中值为 llm_name
521
+ const selectedModel = ref('GPT-4');
522
+ const messageStatus = ref(MessageStatus.Complete);
523
+ const models: IModelOption[] = [
524
+ { id: 1, llm_name: 'GPT-4', property: { support_thinking: true } },
525
+ { id: 2, llm_name: 'Claude 3', property: {} },
526
+ ];
527
+
528
+ const handleSendMessage = async (content: string, docSchema: TagSchema) => {
529
+ /* 发送时可读取 selectedModel.value */
530
+ };
531
+
532
+ const handleModelChange = (model: IModelOption) => {
533
+ console.log('切换模型:', model);
534
+ };
535
+ </script>
536
+ ```
537
+
538
+ 也可通过 `#model-selector` 插槽完全自定义选择器,插槽参数为 `{ models, selectedModel }`:
539
+
540
+ ```vue
541
+ <template>
542
+ <ChatInput v-model="inputValue" :models="models">
543
+ <template #model-selector="{ models, selectedModel }">
544
+ <span>当前:{{ selectedModel || '未选择' }}(共 {{ models.length }} 个)</span>
545
+ </template>
546
+ </ChatInput>
547
+ </template>
548
+ ```
549
+
550
+ ## Expose(模板引用)
551
+
552
+ 通过 `ref` 获取组件实例后可调用以下方法:
553
+
554
+ ```vue
555
+ <template>
556
+ <ChatInput
557
+ ref="chatInputRef"
558
+ v-model="inputValue"
559
+ :on-send-message="handleSendMessage"
560
+ />
561
+ <button @click="chatInputRef?.focus()">聚焦输入框</button>
562
+ <button @click="chatInputRef?.triggerSendMessage()">手动发送</button>
563
+ </template>
564
+
565
+ <script setup lang="ts">
566
+ import { useTemplateRef } from 'vue';
567
+ import { ChatInput } from '@blueking/chat-x';
568
+
569
+ const chatInputRef = useTemplateRef<InstanceType<typeof ChatInput>>('chatInputRef');
570
+ const inputValue = ref('');
571
+ const handleSendMessage = async (content: string) => {
572
+ /* ... */
573
+ };
574
+ </script>
575
+ ```
576
+
577
+ ## API
578
+
579
+ ### Props
580
+
581
+ | 属性名 | 类型 | 默认值 | 必填 | 说明 |
582
+ | ------------------ | -------------------------------------------------------------------------- | -------- | ---- | ------------------------------------------------------- |
583
+ | modelValue | `string \| TagSchema` | - | ✅ | 编辑器的值,支持 `v-model` |
584
+ | selectedModel | `string` | - | - | 当前选中模型的 `llm_name`,支持 `v-model:selected-model` |
585
+ | messageStatus | `MessageStatus` | - | - | 消息状态,控制按钮;输入为空时内部强制 `disabled` |
586
+ | cite | `string` | `''` | - | 引用内容,支持 `v-model:cite`,不为空时显示引用区 |
587
+ | skills | `ISkillListItem[]` | `[]` | - | Skill 列表,输入 `/` 触发,选中后插入 Skill 标签 |
588
+ | prompts | `string[]` | `[]` | - | Prompt 模板列表,输入 `\` 触发 |
589
+ | resources | `IAiSlashMenuItem[]` | `[]` | - | 资源列表,输入 `@` 触发,按 `type` 分组展示 |
590
+ | shortcuts | `Shortcut[]` | - | - | 快捷指令列表,显示在底部工具栏 |
591
+ | models | `IModelOption[]` | - | - | 可选模型列表,传入后在发送按钮左侧展示模型选择器 |
592
+ | shortcutId | `string` | - | - | 当前选中的快捷指令 ID,匹配时列表收起为已选样式 |
593
+ | placeholder | `string` | 动态默认 | - | 编辑器占位符,支持多行;未传时按 skills/prompts/resources 动态拼接 |
594
+ | inputMaxHeight | `number` | `280` | - | 框体最大高度(px),有文件时自动加上文件预览区高度 |
595
+ | defaultUploadFiles | `UploadFile[]` | - | - | 预设已上传的文件列表 |
596
+ | sendDisabledTip | `string` | - | - | 业务阻塞发送时的 tooltip 提示;传入后发送按钮置灰,点击、Enter 与 `triggerSendMessage()` 均不会发送 |
597
+ | supportUpload | `boolean` | `true` | - | 是否显示文件上传按钮 |
598
+ | tippyOptions | `AITippyProps` | — | - | 透传给 FileUploadBtn 和 InputAttachment 的 tooltip 配置 |
599
+ | onSendMessage | `(content: UserMessage['content'], docSchema: TagSchema, options?: { interrupt?: Interrupt; payload?: InterruptResume }) => Promise<void>` | - | - | 发送消息回调,无文件时 content 为字符串,有文件时为数组;经 [ChatContainer](/components/setup/chat-container) 使用时,存在待回答 UserQuestion 会传入第三参数 `options` |
600
+ | onStopSending | `() => Promise<void>` | - | - | 停止发送回调,点击停止按钮时触发 |
601
+ | onUpload | `(file: File) => Promise<{ download_url?: string }>` | - | - | 文件上传回调(每次单文件) |
602
+
603
+ ### 默认占位符
604
+
605
+ 未传入 `placeholder` 时,根据当前 `skills` / `prompts` / `resources` 是否非空动态拼接(有对应能力才显示该行),始终保留换行提示:
606
+
607
+ ```
608
+ 输入 "/" 唤出 Skill // 仅当 skills 非空
609
+ 输入 "\" 唤出 Prompt // 仅当 prompts 非空
610
+ 输入 "@" 唤出 工具和 MCP // 仅当 resources 非空
611
+ 通过 Shift + Enter 进行换行输入 // 始终显示
612
+ ```
613
+
614
+ 显式传入 `placeholder`(含空字符串)时完全覆盖上述默认文案。三种列表都为空时,只显示换行提示。
615
+
616
+ ### Events
617
+
618
+ | 事件名 | 参数 | 触发时机 |
619
+ | ----------------- | ------------------------------------------------------------------------ | --------------------------------------------------------- |
620
+ | update:modelValue | `(value: string \| TagSchema, selectedResourceList: IAiSlashMenuItem[])` | 编辑器值变化时触发;第二个参数为当前已选中的 `@` 资源列表 |
621
+ | modelChange | `(model: IModelOption)` | 用户切换模型时触发 |
622
+ | selectShortcut | `(shortcut: Shortcut)` | 点击底部快捷指令按钮 |
623
+ | deleteShortcut | - | 点击已选快捷指令旁的关闭按钮 |
624
+
625
+ ### Slots
626
+
627
+ | 插槽名 | 参数 | 说明 |
628
+ | ------------ | ---------------------------------- | ---------------------------------------------------------- |
629
+ | top | - | 框体(`.chat-input`)外部顶部,适合展示模型/Token 信息 |
630
+ | interrupt | - | 框体外部顶部,位于 `top` 后,适合展示审批/中断提示 |
631
+ | input-header | - | 框体内顶部,替换引用区(`CiteContent`) |
632
+ | files | `{ files: Partial<UploadFile>[] }` | 文件预览区 |
633
+ | attachment | - | 底部快捷指令区,`FileUploadBtn` 在其左侧,不受此 slot 影响 |
634
+ | model-selector | `{ models: IModelOption[]; selectedModel: string \| undefined }` | 发送按钮左侧模型选择区,默认渲染 ModelSelector |
635
+ | send-icon | - | 发送按钮内图标,按钮的点击逻辑和样式仍由组件控制 |
636
+
637
+ ### Expose
638
+
639
+ | 方法名 | 类型 | 说明 |
640
+ | ------------------ | ------------ | ---------------- |
641
+ | focus | `() => void` | 聚焦编辑器 |
642
+ | triggerSendMessage | `() => void` | 手动触发发送逻辑 |
643
+
644
+ ## 键盘快捷键
645
+
646
+ | 快捷键 | 说明 |
647
+ | --------------- | ---------------------------------------- |
648
+ | `Enter` | 发送消息(输入为空或仅空白字符时不触发) |
649
+ | `Shift + Enter` | 换行 |
650
+ | `/` | 唤出 Prompt 列表 |
651
+ | `@` | 唤出资源列表 |
652
+ | `↑` / `↓` | 在 Prompt / 资源菜单中导航 |
653
+ | `Esc` | 关闭 Prompt / 资源菜单 |
654
+
655
+ ## 类型定义
656
+
657
+ > `MessageStatus` 完整取值见 [常量枚举](../../types/constants)。与输入区相关:`pending` / `streaming` / `fetching` → 停止按钮;`complete` / `completed` / `error` / `stop` → 发送;`disabled` → 置灰。
658
+
659
+ ```typescript
660
+ import type { UserMessage } from '@blueking/chat-x';
661
+
662
+ // 上传状态
663
+ enum UploadStatus {
664
+ Pending = 'pending', // 上传中
665
+ Success = 'success', // 上传成功
666
+ Error = 'error', // 上传失败
667
+ }
668
+
669
+ // 上传文件
670
+ type UploadFile = {
671
+ type: 'binary';
672
+ url?: string; // 上传成功后的下载地址
673
+ filename?: string; // 文件名
674
+ mimeType?: string; // MIME 类型
675
+ file?: File; // 原始 File 对象
676
+ status?: UploadStatus; // 上传状态
677
+ };
678
+
679
+ // 资源菜单项(@ 触发)
680
+ interface IAiSlashMenuItem {
681
+ id: string;
682
+ name: string;
683
+ icon: string;
684
+ type: 'tool' | 'mcp' | 'doc' | 'shortcut'; // 分组类型
685
+ }
686
+
687
+ // onSendMessage 的 content 参数
688
+ type SendContent =
689
+ | string // 无文件时:纯文本
690
+ | Array<
691
+ // 有文件时:数组
692
+ { type: 'binary'; url?: string; mimeType: string; filename: string } | { type: 'text'; text: string }
693
+ >;
694
+
695
+ // onSendMessage 完整签名(第三参数由 ChatContainer 在 UserQuestion 场景注入)
696
+ type OnSendMessage = (
697
+ content: SendContent,
698
+ docSchema: TagSchema,
699
+ options?: { interrupt?: Interrupt; payload?: InterruptResume },
700
+ ) => Promise<void>;
701
+ ```
702
+
703
+ ## 完整集成示例
704
+
705
+ ```vue
706
+ <template>
707
+ <ChatInput
708
+ v-model="inputValue"
709
+ v-model:cite="citeContent"
710
+ :message-status="messageStatus"
711
+ :prompts="prompts"
712
+ :resources="resources"
713
+ :shortcuts="shortcuts"
714
+ :shortcut-id="shortcutId"
715
+ :on-send-message="handleSendMessage"
716
+ :on-stop-sending="handleStopSending"
717
+ :on-upload="handleUpload"
718
+ @select-shortcut="shortcutId = $event.id"
719
+ @delete-shortcut="shortcutId = ''"
720
+ />
721
+ </template>
722
+
723
+ <script setup lang="ts">
724
+ import { ref } from 'vue';
725
+ import { ChatInput, MessageStatus, type TagSchema, type Shortcut } from '@blueking/chat-x';
726
+
727
+ const inputValue = ref('');
728
+ const citeContent = ref('');
729
+ const messageStatus = ref(MessageStatus.Complete);
730
+ const shortcutId = ref('');
731
+
732
+ const prompts = ['帮我写一篇关于 {topic} 的文章', '解释一下这段代码'];
733
+ const resources = [{ id: 'tool1', name: '天气查询', type: 'tool', icon: '' }];
734
+ const shortcuts: Shortcut[] = [
735
+ { id: 'translate', name: '翻译' },
736
+ { id: 'explain', name: '解释' },
737
+ ];
738
+
739
+ const handleSendMessage = async (content, docSchema: TagSchema) => {
740
+ messageStatus.value = MessageStatus.Streaming;
741
+ try {
742
+ await callAI(content, { cite: citeContent.value, shortcut: shortcutId.value });
743
+ } finally {
744
+ messageStatus.value = MessageStatus.Complete;
745
+ citeContent.value = '';
746
+ }
747
+ };
748
+
749
+ const handleStopSending = async () => {
750
+ messageStatus.value = MessageStatus.Stop;
751
+ abortAICall();
752
+ };
753
+
754
+ const handleUpload = async (file: File) => {
755
+ const formData = new FormData();
756
+ formData.append('file', file);
757
+ const res = await fetch('/api/upload', { method: 'POST', body: formData });
758
+ return res.json(); // { download_url: '...' }
759
+ };
760
+ </script>
761
+ ```
762
+
763
+ ## 关联组件
764
+
765
+ - [ShortcutBtns](/components/input/shortcut-btns) — 底部附件区默认快捷指令列表
766
+ - [ShortcutBtn](/components/input/shortcut-btn) — 已选快捷指令单按钮展示
767
+ - [ShortcutRender](/components/input/shortcut-render) — 含表单的快捷指令表单渲染
768
+ - [ChatContainer](/components/setup/chat-container) — 顶层布局中包裹输入区
769
+ - [CiteContent](/components/rendering/cite-content) — 消息引用区内容展示