开发预览 更新于 2026-05-10

PromptComposer 提示输入器

多行 chat 输入:附件、slash 命令、@ mention、submitKey、loading 时 stop 按钮。

基础用法

输入 / 在行首或空格后弹出 slash 命令,输入 @ 弹出 mention 列表,键盘 ↑/↓ 选择,Enter / Tab 选中。默认 Enter 提交、Shift+Enter 换行;想用 IDE 风格的 Cmd/Ctrl+Enter 提交,传 submitKey="mod-enter"loading=true 时发送按钮变成红色 stop 按钮,触发 stop 事件。

背景 视口
Enter 发送 · Shift+Enter 换行

试一下:输入 / 看 slash 命令;输入 @ 看 mention 列表。

src/App.vue
<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 / valuestring''输入内容
placeholderstring'输入消息...'占位
disabledbooleanfalse禁用
loadingbooleanfalse显示 stop;禁用 Enter 提交
maxLengthnumber字符上限
maxRowsnumber8自动高度上限
submitKey'enter' | 'mod-enter''enter'提交快捷键
attachmentsPromptAttachment[][]附件预览(受控)
slashCommandsSlashCommand[][]/ 弹出列表
mentionsMentionItem[][]@ 弹出列表

Events / callbacks

VueReactpayload
submitonSubmit(text: string)
stoponStop
attach-addonAttachAdd(file: File)
attach-removeonAttachRemove(id: string)
mentiononMentionMentionItem
slashonSlashSlashCommand

Slash 与 mention 数据形状

interface SlashCommand {
  id: string;
  label: string;
  description?: string;
  match?: string[];   // 别名匹配
}

interface MentionItem {
  id: string;
  label: string;
  description?: string;
  avatar?: string;
}

反馈与讨论

PromptComposer 提示输入器 的讨论

0
0 / 600
正在加载评论...