PromptComposer 提示输入器
多行 chat 输入:附件、slash 命令、@ mention、submitKey、loading 时 stop 按钮。
English translation pending This page hasn't been translated yet — falling back to Chinese. PRs welcome on GitHub.
基础用法
输入 / 在行首或空格后弹出 slash 命令,输入 @ 弹出 mention 列表,键盘 ↑/↓ 选择,Enter / Tab 选中。默认 Enter 提交、Shift+Enter 换行;想用 IDE 风格的 Cmd/Ctrl+Enter 提交,传 submitKey="mod-enter"。loading=true 时发送按钮变成红色 stop 按钮,触发 stop 事件。
背景 视口
试一下:输入 / 看 slash 命令;输入 @ 看 mention 列表。
<script setup lang="ts">
import { ref } from 'vue';
import { CfPromptComposer, type PromptAttachment, type SlashCommand, type MentionItem } from '@chufix-design/vue';
const value = ref('');
const loading = ref(false);
const attachments = ref<PromptAttachment[]>([]);
const log = ref('');
const slashCommands: SlashCommand[] = [
{ id: 'summarize', label: 'summarize', description: '总结当前对话' },
{ id: 'translate', label: 'translate', description: '翻译为指定语言' },
{ id: 'web', label: 'web', description: '调用 Web 搜索', match: ['search', '搜索'] },
];
const mentions: MentionItem[] = [
{ id: 'sarah', label: 'Sarah', description: 'Designer' },
{ id: 'yifan', label: 'Yifan', description: 'Engineer' },
{ id: 'lin', label: 'Lin', description: 'PM' },
];
function onSubmit(text: string) {
log.value = `提交:${text}`;
loading.value = true;
setTimeout(() => {
loading.value = false;
value.value = '';
attachments.value = [];
}, 1200);
}
function onAttachAdd(file: File) {
attachments.value.push({
id: `${Date.now()}-${file.name}`,
name: file.name,
size: file.size,
mime: file.type,
});
}
function onAttachRemove(id: string) {
attachments.value = attachments.value.filter((a) => a.id !== id);
}
</script>
<template>
<div class="demo-stack">
<CfPromptComposer
v-model="value"
:loading="loading"
:attachments="attachments"
:slash-commands="slashCommands"
:mentions="mentions"
@submit="onSubmit"
@stop="loading = false"
@attach-add="onAttachAdd"
@attach-remove="onAttachRemove"
/>
<p v-if="log" class="demo-hint">{{ log }}</p>
<p class="demo-hint">试一下:输入 <code>/</code> 看 slash 命令;输入 <code>@</code> 看 mention 列表。</p>
</div>
</template> <script setup>
import { ref } from 'vue';
import { CfPromptComposer } from '@chufix-design/vue';
const value = ref('');
const loading = ref(false);
const attachments = ref<PromptAttachment[]>([]);
const log = ref('');
const slashCommands= [
{ id: 'summarize', label: 'summarize', description: '总结当前对话' },
{ id: 'translate', label: 'translate', description: '翻译为指定语言' },
{ id: 'web', label: 'web', description: '调用 Web 搜索', match: ['search', '搜索'] },
];
const mentions= [
{ id: 'sarah', label: 'Sarah', description: 'Designer' },
{ id: 'yifan', label: 'Yifan', description: 'Engineer' },
{ id: 'lin', label: 'Lin', description: 'PM' },
];
function onSubmit(text) {
log.value = `提交:${text}`;
loading.value = true;
setTimeout(() => {
loading.value = false;
value.value = '';
attachments.value = [];
}, 1200);
}
function onAttachAdd(file) {
attachments.value.push({
id: `${Date.now()}-${file.name}`,
name: file.name,
size: file.size,
mime: file.type,
});
}
function onAttachRemove(id) {
attachments.value = attachments.value.filter((a) => a.id !== id);
}
</script>
<template>
<div class="demo-stack">
<CfPromptComposer
v-model="value"
:loading="loading"
:attachments="attachments"
:slash-commands="slashCommands"
:mentions="mentions"
@submit="onSubmit"
@stop="loading = false"
@attach-add="onAttachAdd"
@attach-remove="onAttachRemove"
/>
<p v-if="log" class="demo-hint">{{ log }}</p>
<p class="demo-hint">试一下:输入 <code>/</code> 看 slash 命令;输入 <code>@</code> 看 mention 列表。</p>
</div>
</template> import { useState } from 'react';
import { CfPromptComposer } from '@chufix-design/react';
export default function Demo() {
const [value, setValue] = useState('');
const [loading, setLoading] = useState(false);
const [attachments, setAttachments] = useState<PromptAttachment[]>([]);
const [log, setLog] = useState('');
const slashCommands: SlashCommand[] = [
{ id: 'summarize', label: 'summarize', description: '总结当前对话' },
{ id: 'translate', label: 'translate', description: '翻译为指定语言' },
{ id: 'web', label: 'web', description: '调用 Web 搜索', match: ['search', '搜索'] },
];
const mentions: MentionItem[] = [
{ id: 'sarah', label: 'Sarah', description: 'Designer' },
{ id: 'yifan', label: 'Yifan', description: 'Engineer' },
{ id: 'lin', label: 'Lin', description: 'PM' },
];
function onSubmit(text: string) {
setLog(`提交:${text}`);
setLoading(true);
setTimeout(() => {
setLoading(false);
setValue('');
setAttachments([]);
}, 1200);
}
function onAttachAdd(file: File) {
attachments.push({
id: `${Date.now()}-${file.name}`,
name: file.name,
size: file.size,
mime: file.type,
});
}
function onAttachRemove(id: string) {
setAttachments(attachments.filter((a) => a.id !== id));
}
return (
<>
<div className="demo-stack">
<CfPromptComposer value={value} onChange={setValue} loading={loading} attachments={attachments} slashCommands={slashCommands} mentions={mentions} onSubmit={onSubmit} onStop={() => setLoading(false)}
onAttachAdd={onAttachAdd}
onAttachRemove={onAttachRemove}
/>
<p v-if="log" className="demo-hint">{log}</p>
<p className="demo-hint">试一下:输入 <code>/</code> 看 slash 命令;输入 <code>@</code> 看 mention 列表。</p>
</div>
</>
);
} import { useState } from 'react';
import { CfPromptComposer } from '@chufix-design/react';
export default function Demo() {
const [value, setValue] = useState('');
const [loading, setLoading] = useState(false);
const [attachments, setAttachments] = useState<PromptAttachment[]>([]);
const [log, setLog] = useState('');
const slashCommands= [
{ id: 'summarize', label: 'summarize', description: '总结当前对话' },
{ id: 'translate', label: 'translate', description: '翻译为指定语言' },
{ id: 'web', label: 'web', description: '调用 Web 搜索', match: ['search', '搜索'] },
];
const mentions= [
{ id: 'sarah', label: 'Sarah', description: 'Designer' },
{ id: 'yifan', label: 'Yifan', description: 'Engineer' },
{ id: 'lin', label: 'Lin', description: 'PM' },
];
function onSubmit(text) {
setLog(`提交:${text}`);
setLoading(true);
setTimeout(() => {
setLoading(false);
setValue('');
setAttachments([]);
}, 1200);
}
function onAttachAdd(file) {
attachments.push({
id: `${Date.now()}-${file.name}`,
name: file.name,
size: file.size,
mime: file.type,
});
}
function onAttachRemove(id) {
setAttachments(attachments.filter((a) => a.id !== id));
}
return (
<>
<div className="demo-stack">
<CfPromptComposer value={value} onChange={setValue} loading={loading} attachments={attachments} slashCommands={slashCommands} mentions={mentions} onSubmit={onSubmit} onStop={() => setLoading(false)}
onAttachAdd={onAttachAdd}
onAttachRemove={onAttachRemove}
/>
<p v-if="log" className="demo-hint">{log}</p>
<p className="demo-hint">试一下:输入 <code>/</code> 看 slash 命令;输入 <code>@</code> 看 mention 列表。</p>
</div>
</>
);
} API
| 属性 | 类型 | 默认 | 说明 |
|---|---|---|---|
modelValue / value | string | '' | 输入内容 |
placeholder | string | '输入消息...' | 占位 |
disabled | boolean | false | 禁用 |
loading | boolean | false | 显示 stop;禁用 Enter 提交 |
maxLength | number | — | 字符上限 |
maxRows | number | 8 | 自动高度上限 |
submitKey | 'enter' | 'mod-enter' | 'enter' | 提交快捷键 |
attachments | PromptAttachment[] | [] | 附件预览(受控) |
slashCommands | SlashCommand[] | [] | / 弹出列表 |
mentions | MentionItem[] | [] | @ 弹出列表 |
Events / callbacks
| Vue | React | payload |
|---|---|---|
submit | onSubmit | (text: string) |
stop | onStop | — |
attach-add | onAttachAdd | (file: File) |
attach-remove | onAttachRemove | (id: string) |
mention | onMention | MentionItem |
slash | onSlash | SlashCommand |
Slash 与 mention 数据形状
interface SlashCommand {
id: string;
label: string;
description?: string;
match?: string[]; // 别名匹配
}
interface MentionItem {
id: string;
label: string;
description?: string;
avatar?: string;
}
反馈与讨论
PromptComposer 提示输入器 · Discussion