123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863 |
- <script setup lang="tsx">
- import {
- Attachments,
- type AttachmentsProps,
- Bubble,
- type BubbleListProps,
- Conversations,
- type ConversationsProps,
- Prompts,
- type PromptsProps,
- Sender,
- Welcome,
- useXAgent,
- useXChat,
- } from 'ant-design-x-vue';
- import {
- CloudUploadOutlined,
- CommentOutlined,
- EllipsisOutlined,
- FireOutlined,
- HeartOutlined,
- PaperClipOutlined,
- PlusOutlined,
- ReadOutlined,
- ShareAltOutlined,
- SmileOutlined,
- } from '@ant-design/icons-vue';
- import { Badge, Button, Space, theme } from 'ant-design-vue';
- import { computed, ref, watch, type VNode, h, onMounted } from 'vue';
- import MarkdownIt from 'markdown-it';
- defineOptions({ name: 'PlaygroundIndependent' });
- const sleep = () => new Promise((resolve) => setTimeout(resolve, 500));
- const renderTitle = (icon: VNode, title: string) => (
- <Space align="start">
- {icon}
- <span>{title}</span>
- </Space>
- );
- const defaultConversationsItems = [
- {
- key: '0',
- label: 'What is Ant Design X?',
- },
- ];
- const placeholderPromptsItems: PromptsProps['items'] = [
- {
- key: '1',
- label: renderTitle(<FireOutlined style={{ color: '#FF4D4F' }} />, 'Hot Topics'),
- description: 'What are you interested in?',
- children: [
- {
- key: '1-1',
- description: `What's new in X?`,
- },
- {
- key: '1-2',
- description: `What's AGI?`,
- },
- {
- key: '1-3',
- description: `Where is the doc?`,
- },
- ],
- },
- {
- key: '2',
- label: renderTitle(<ReadOutlined style={{ color: '#1890FF' }} />, 'Design Guide'),
- description: 'How to design a good product?',
- children: [
- {
- key: '2-1',
- icon: <HeartOutlined />,
- description: `Know the well`,
- },
- {
- key: '2-2',
- icon: <SmileOutlined />,
- description: `Set the AI role`,
- },
- {
- key: '2-3',
- icon: <CommentOutlined />,
- description: `Express the feeling`,
- },
- ],
- },
- ];
- const senderPromptsItems: PromptsProps['items'] = [
- {
- key: '1',
- description: 'Hot Topics',
- icon: <FireOutlined style={{ color: '#FF4D4F' }} />,
- },
- {
- key: '2',
- description: 'Design Guide',
- icon: <ReadOutlined style={{ color: '#1890FF' }} />,
- },
- ];
- const roles: BubbleListProps['roles'] = {
- ai: {
- placement: 'start',
- typing: { step: 5, interval: 20 },
- styles: {
- content: {
- borderRadius: '16px',
- },
- },
- },
- local: {
- placement: 'end',
- variant: 'shadow',
- },
- };
- // ==================== Style ====================
- const { token } = theme.useToken();
- const styles = computed(() => {
- return {
- layout: {
- width: '100%',
- 'min-width': '1000px',
- height: '722px',
- 'border-radius': `${token.value.borderRadius}px`,
- display: 'flex',
- background: `${token.value.colorBgContainer}`,
- 'font-family': `AlibabaPuHuiTi, ${token.value.fontFamily}, sans-serif`,
- },
- menu: {
- background: `${token.value.colorBgLayout}80`,
- width: '280px',
- height: '100%',
- display: 'flex',
- 'flex-direction': 'column',
- },
- conversations: {
- padding: '0 12px',
- flex: 1,
- 'overflow-y': 'auto',
- },
- chat: {
- height: '100%',
- width: '100%',
- 'max-width': '700px',
- margin: '0 auto',
- 'box-sizing': 'border-box',
- display: 'flex',
- 'flex-direction': 'column',
- padding: `${token.value.paddingLG}px`,
- gap: '16px',
- },
- messages: {
- flex: 1,
- },
- placeholder: {
- 'padding-top': '32px',
- },
- sender: {
- 'box-shadow': token.value.boxShadow,
- },
- logo: {
- display: 'flex',
- height: '72px',
- 'align-items': 'center',
- 'justify-content': 'start',
- padding: '0 24px',
- 'box-sizing': 'border-box',
- },
- 'logo-img': {
- width: '24px',
- height: '24px',
- display: 'inline-block',
- },
- 'logo-span': {
- display: 'inline-block',
- margin: '0 8px',
- 'font-weight': 'bold',
- color: token.value.colorText,
- 'font-size': '16px',
- },
- addBtn: {
- background: '#1677ff0f',
- border: '1px solid #1677ff34',
- width: 'calc(100% - 24px)',
- margin: '0 12px 24px 12px',
- }
- } as const
- });
- // ==================== State ====================
- const headerOpen = ref(false);
- const content = ref('');
- const conversationsItems = ref(defaultConversationsItems);
- const activeKey = ref(defaultConversationsItems[0].key);
- const attachedFiles = ref<AttachmentsProps['items']>([]);
- const agentRequestLoading = ref(false);
- const pagination = ref({
- total: 0,
- page: 1,
- pageSize: 20,
- totalPages: 0
- });
- const loadingHistory = ref(false);
- // 添加文件类型分类存储
- const imageUrls = ref<string[]>([]);
- const documentUrls = ref<string[]>([]);
- // ==================== Runtime ====================
- const [ agent ] = useXAgent({
- request: async ({ message, attachments }, { onSuccess, onError }) => {
- agentRequestLoading.value = true;
-
- try {
- // 调用多模态聊天接口
- const apiUrl = `http://121.36.251.245:8082/chatbot/multimodal`;
- const response = await fetch(apiUrl, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- message: message,
- chat_config_id: "17",
- user_id: 4,
- session_id: "",
- source: "pc",
- image_urls: imageUrls.value,
- documents: documentUrls.value,
- merchant_id: "3",
- model_type: "REMOTE_CLAUDE",
- model_name: "claude-3-sonnet-20240229"
- }),
- });
-
- if (!response.ok) {
- throw new Error(`API request failed with status ${response.status}`);
- }
-
- const responseData = await response.json();
-
- // 检查响应状态码
- if (responseData.code !== 2000) {
- throw new Error(`API returned error code: ${responseData.code}, message: ${responseData.message}`);
- }
-
- // 从响应中提取内容
- if (responseData.choices && responseData.choices[0].message.content) {
- // 将AI回复内容传递给onSuccess
- onSuccess(responseData.choices[0].message.content);
-
- // 可选:记录模型信息和token使用情况
- console.log(`Model used: ${responseData.data.model}`);
- if (responseData.usage) {
- console.log(`Token usage: ${JSON.stringify(responseData.usage)}`);
- }
- } else {
- throw new Error('Response data is missing content');
- }
- } catch (error) {
- console.error('Error calling multimodal API:', error);
- onError(error instanceof Error ? error : new Error('Unknown error occurred'));
- } finally {
- agentRequestLoading.value = false;
- // 清空URL数组
- imageUrls.value = [];
- documentUrls.value = [];
- }
- },
- });
- const { onRequest, messages, setMessages } = useXChat({
- agent: agent.value,
- onRequest: (message, attachments) => {
- return agent.value.request({
- message,
- attachments,
- });
- }
- });
- watch(activeKey, () => {
- if (activeKey.value !== undefined) {
- setMessages([]);
- }
- }, { immediate: true });
- // ==================== Event ====================
- const onSubmit = (nextContent: string) => {
- if (!nextContent) return;
-
- // 检查是否有正在上传的文件
- const hasUploadingFiles = attachedFiles.value.some(file => file.status === 'uploading');
- if (hasUploadingFiles) {
- // 可以显示一个提示,告诉用户等待文件上传完成
- console.warn('Please wait for file uploads to complete');
- return;
- }
-
- // 将附件文件传递给请求,同时传递分类后的URLs
- onRequest(nextContent, attachedFiles.value);
-
- // 发送后清空输入和附件
- content.value = '';
- attachedFiles.value = [];
- // 清空URL数组
- imageUrls.value = [];
- documentUrls.value = [];
- headerOpen.value = false;
- };
- const onPromptsItemClick: PromptsProps['onItemClick'] = (info) => {
- onRequest(info.data.description as string);
- };
- const onAddConversation = () => {
- // 添加一个特殊的"新会话"选项
- const newConversationKey = 'new';
-
- // 检查是否已经有新会话选项
- if (!conversationsItems.value.some(item => item.key === newConversationKey)) {
- conversationsItems.value = [
- {
- key: newConversationKey,
- label: '新会话',
- },
- ...conversationsItems.value,
- ];
- }
-
- // 选中新会话
- activeKey.value = newConversationKey;
- // 清空消息
- setMessages([]);
- };
- const onConversationClick: ConversationsProps['onActiveChange'] = async (key) => {
- activeKey.value = key;
-
- // 如果是新会话,清空消息
- if (key === 'new') {
- setMessages([]);
- return;
- }
-
- // 查找选中的会话
- const selectedSession = conversationsItems.value.find(item => item.key === key);
- if (!selectedSession) return;
-
- // 这里可以添加加载特定会话消息的逻辑
- // 例如:
- try {
- const response = await fetch(`http://121.36.251.245:8082/api/chat/messages?session_id=${key}`, {
- method: 'GET',
- headers: {
- 'Content-Type': 'application/json',
- }
- });
-
- if (!response.ok) {
- throw new Error(`Failed to fetch messages: ${response.status}`);
- }
-
- const result = await response.json();
- if (result.code !== 2000) {
- throw new Error(`API returned error: ${result.message}`);
- }
-
- // 假设API返回的消息格式与您的消息格式兼容
- if (result.data && result.data.messages) {
- // 转换消息格式
- const formattedMessages = result.data.messages.map((msg: any) => ({
- id: msg.id || Math.random().toString(36).substring(2, 9),
- message: msg.content,
- status: msg.role === 'assistant' ? 'ai' : 'local'
- }));
-
- setMessages(formattedMessages);
- }
- } catch (error) {
- console.error('Error fetching messages:', error);
- // 如果获取失败,清空消息
- setMessages([]);
- }
- };
- const handleFileChange: AttachmentsProps['onChange'] = (info) => {
- console.log('File change event:', info);
-
- // 获取新添加的文件
- const newFile = info.file;
-
- // 保存文件列表,包括原始文件对象
- attachedFiles.value = info.fileList.map(file => ({
- ...file,
- // 确保保留原始文件对象以便上传
- originFileObj: file.originFileObj
- }));
-
- // 检查是否有新文件添加
- if (newFile && newFile.status === 'uploading' && newFile.originFileObj) {
- console.log('Starting upload for file:', newFile.name);
-
- // 立即上传文件
- uploadFile(newFile.originFileObj)
- .then(fileUrl => {
- console.log('File uploaded successfully:', fileUrl);
- // 更新文件状态为已上传成功
- attachedFiles.value = attachedFiles.value.map(file => {
- if (file.uid === newFile.uid) {
- return {
- ...file,
- status: 'done',
- url: fileUrl,
- response: { url: fileUrl }
- };
- }
- return file;
- });
- })
- .catch(error => {
- console.error('File upload failed:', error);
- // 更新文件状态为上传失败
- attachedFiles.value = attachedFiles.value.map(file => {
- if (file.uid === newFile.uid) {
- return {
- ...file,
- status: 'error',
- error
- };
- }
- return file;
- });
- });
- }
- };
- // 添加文件上传函数
- const uploadFile = async (file: File): Promise<string> => {
- const formData = new FormData();
- formData.append('file', file);
-
- try {
- const uploadResponse = await fetch('http://121.36.251.245:8082/upload/file', {
- method: 'POST',
- body: formData,
- });
-
- if (!uploadResponse.ok) {
- throw new Error(`File upload failed with status ${uploadResponse.status}`);
- }
-
- const uploadResult = await uploadResponse.json();
-
- // 检查上传响应状态码
- if (uploadResult.status !== 2000) {
- throw new Error(`Upload API returned error: ${uploadResult.message}`);
- }
-
- // 从响应中获取文件URL
- const fileUrl = uploadResult.data?.fileUrl;
-
- if (!fileUrl) {
- throw new Error('Upload response missing file URL');
- }
-
- // 根据文件URL后缀或文件类型判断是否为图片
- const isImage = (url: string, fileType: string): boolean => {
- // 检查URL后缀
- const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'];
- const hasImageExtension = imageExtensions.some(ext => url.toLowerCase().endsWith(ext));
-
- // 检查MIME类型
- const isImageMimeType = fileType.startsWith('image/');
-
- return hasImageExtension || isImageMimeType;
- };
-
- // 根据判断结果分类存储URL
- if (isImage(fileUrl, file.type)) {
- imageUrls.value.push(fileUrl);
- console.log('Added image URL:', fileUrl);
- console.log('Current image URLs:', imageUrls.value);
- } else {
- documentUrls.value.push(fileUrl);
- console.log('Added document URL:', fileUrl);
- console.log('Current document URLs:', documentUrls.value);
- }
-
- return fileUrl;
- } catch (error) {
- console.error('Error uploading file:', error);
- throw error;
- }
- };
- // ==================== Nodes ====================
- const placeholderNode = computed(() => (
- <Space direction="vertical" size={16} style={styles.value.placeholder}>
- <Welcome
- variant="borderless"
- icon="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp"
- title="Hello, I'm Ant Design X"
- description="Base on Ant Design, AGI product interface solution, create a better intelligent vision~"
- extra={
- <Space>
- <Button icon={<ShareAltOutlined />} />
- <Button icon={<EllipsisOutlined />} />
- </Space>
- }
- />
- <Prompts
- title="Do you want?"
- items={placeholderPromptsItems}
- styles={{
- list: {
- width: '100%',
- },
- item: {
- flex: 1,
- },
- }}
- onItemClick={onPromptsItemClick}
- />
- </Space>
- ));
- // 创建 markdown-it 实例
- const md = new MarkdownIt({
- html: true,
- linkify: true,
- typographer: true
- });
- const items = computed<BubbleListProps['items']>(() => messages.value.map(({ id, message, status }) => {
- // 检查消息是否是 markdown 格式
- const isMarkdown = status === 'ai' &&
- (message.includes('#') || message.includes('**') || message.includes('\n'));
-
- let content;
-
- if (isMarkdown && status === 'ai') {
- // 渲染 markdown 内容
- const htmlContent = md.render(message);
- content = h('div', {
- class: 'markdown-content',
- innerHTML: htmlContent
- });
- } else {
- // 普通文本内容
- content = message;
- }
-
- return {
- key: id,
- loading: status === 'loading',
- role: status === 'local' ? 'local' : 'ai',
- content: content,
- };
- }));
- const attachmentsNode = computed(() => (
- <Badge dot={attachedFiles.value.length > 0 && !headerOpen.value}>
- <Button type="text" icon={<PaperClipOutlined />} onClick={() => headerOpen.value = !headerOpen.value} />
- </Badge>
- ));
- const senderHeader = computed(() => (
- <Sender.Header
- title="Attachments"
- open={headerOpen.value}
- onOpenChange={(open) => headerOpen.value = open}
- styles={{
- content: {
- padding: 0,
- },
- }}
- >
- <Attachments
- action="http://121.36.251.245:8082/upload/file" // 添加上传地址
- name="file" // 指定上传字段名
- items={attachedFiles.value}
- onChange={handleFileChange}
- customRequest={({ file, onSuccess, onError, onProgress }) => {
- // 手动处理文件上传
- if (file && file.originFileObj) {
- console.log('Custom request triggered for file:', file.name);
- uploadFile(file.originFileObj)
- .then(fileUrl => {
- console.log('Custom upload successful:', fileUrl);
- onSuccess({ url: fileUrl }, new XMLHttpRequest());
- })
- .catch(err => {
- console.error('Custom upload failed:', err);
- onError(err);
- });
- }
- }}
- placeholder={(type) =>
- type === 'drop'
- ? { title: 'Drop file here' }
- : {
- icon: <CloudUploadOutlined />,
- title: 'Upload files',
- description: 'Click or drag files to this area to upload',
- }
- }
- />
- </Sender.Header>
- ));
- const logoNode = computed(() => (
- <div style={styles.value.logo}>
- <img
- src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*eco6RrQhxbMAAAAAAAAAAAAADgCCAQ/original"
- draggable={false}
- alt="logo"
- style={styles.value['logo-img']}
- />
- <span style={styles.value['logo-span']}>Ant Design X Vue</span>
- </div>
- ));
- // 添加 markdown 样式
- const markdownStyles = `
- .markdown-content h3 {
- font-size: 1.3em;
- margin-top: 1em;
- margin-bottom: 0.5em;
- font-weight: 600;
- }
- .markdown-content h4 {
- font-size: 1.1em;
- margin-top: 1em;
- margin-bottom: 0.5em;
- font-weight: 600;
- }
- .markdown-content p {
- margin-bottom: 0.8em;
- }
- .markdown-content ul, .markdown-content ol {
- padding-left: 1.5em;
- margin-bottom: 0.8em;
- }
- .markdown-content li {
- margin-bottom: 0.3em;
- }
- .markdown-content strong {
- font-weight: 600;
- }
- `;
- // 修改 fetchChatHistory 函数,处理 last_message 和 user_message
- const fetchChatHistory = async () => {
- loadingHistory.value = true;
- try {
- const response = await fetch('http://121.36.251.245:8082/api/chat/history', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- merchant_id: "3",
- user_id: 4,
- page: pagination.value.page,
- page_size: pagination.value.pageSize
- }),
- });
- if (!response.ok) {
- throw new Error(`获取聊天历史失败: ${response.status}`);
- }
- const result = await response.json();
-
- if (result.code !== 2000) {
- throw new Error(`API返回错误: ${result.message}`);
- }
- // 更新会话列表
- if (result.data && result.data.sessions) {
- conversationsItems.value = result.data.sessions.map((session: any) => {
- // 优先使用用户消息作为会话标签,如果没有则使用AI回复
- const displayContent = session.user_message?.content || session.last_message?.content || '新会话';
-
- return {
- key: session.session_id,
- // 截取前20个字符作为标签,如果超过则添加省略号
- label: displayContent.substring(0, 20) + (displayContent.length > 20 ? '...' : ''),
- // 保存时间戳
- timestamp: new Date(session.last_message?.created_at || Date.now()),
- // 保存用户消息和AI回复,以便在UI中显示
- userMessage: session.user_message,
- aiMessage: session.last_message,
- // 保存完整会话数据以备后用
- sessionData: session
- };
- });
-
- // 如果有会话,默认选中第一个
- if (conversationsItems.value.length > 0) {
- activeKey.value = conversationsItems.value[0].key;
-
- // 加载第一个会话的消息
- await loadSessionMessages(conversationsItems.value[0].key);
- }
-
- // 更新分页信息
- if (result.data.pagination) {
- pagination.value = {
- total: result.data.pagination.total,
- page: result.data.pagination.page,
- pageSize: result.data.pagination.page_size,
- totalPages: result.data.pagination.total_pages
- };
- }
- }
- } catch (error) {
- console.error('获取聊天历史出错:', error);
- // 如果获取失败,使用默认会话列表
- conversationsItems.value = defaultConversationsItems;
- } finally {
- loadingHistory.value = false;
- }
- };
- // 修改加载会话消息的函数,处理用户消息和AI回复
- const loadSessionMessages = async (sessionId: string) => {
- try {
- // 显示加载状态
- setMessages([{ id: 'loading', message: '加载消息中...', status: 'loading' }]);
-
- const response = await fetch(`http://121.36.251.245:8082/api/chat/messages?session_id=${sessionId}`, {
- method: 'GET',
- headers: {
- 'Content-Type': 'application/json',
- }
- });
-
- if (!response.ok) {
- throw new Error(`获取消息失败: ${response.status}`);
- }
-
- const result = await response.json();
- if (result.code !== 2000) {
- throw new Error(`API返回错误: ${result.message}`);
- }
-
- // 处理消息数据
- if (result.data && result.data.messages) {
- // 转换消息格式并按时间排序
- const formattedMessages = [];
-
- // 遍历消息,每组包含用户消息和AI回复
- for (const msg of result.data.messages) {
- // 如果有用户消息,添加到列表
- if (msg.user_message && msg.user_message.content) {
- formattedMessages.push({
- id: `user_${msg.id || Math.random().toString(36).substring(2, 9)}`,
- message: msg.user_message.content,
- status: 'local',
- timestamp: new Date(msg.user_message.created_at || Date.now())
- });
- }
-
- // 如果有AI回复,添加到列表
- if (msg.last_message && msg.last_message.content) {
- formattedMessages.push({
- id: `ai_${msg.id || Math.random().toString(36).substring(2, 9)}`,
- message: msg.last_message.content,
- status: 'ai',
- timestamp: new Date(msg.last_message.created_at || Date.now())
- });
- }
- }
-
- // 按时间排序
- formattedMessages.sort((a, b) => a.timestamp - b.timestamp);
-
- // 更新消息列表
- setMessages(formattedMessages);
- } else {
- // 如果没有消息,显示空列表
- setMessages([]);
- }
- } catch (error) {
- console.error('加载会话消息出错:', error);
- // 显示错误消息
- setMessages([{
- id: 'error',
- message: '加载消息失败,请重试',
- status: 'ai'
- }]);
- }
- };
- // 在组件挂载时获取聊天历史
- onMounted(() => {
- /* fetchChatHistory(); */
- });
- defineRender(() => {
- return (
- <div style={styles.value.layout}>
- {/* 添加 markdown 样式 */}
- <style>{markdownStyles}</style>
-
- <div style={styles.value.menu}>
- {/* 🌟 Logo */}
- {logoNode.value}
- {/* 🌟 添加会话 */}
- <Button
- onClick={onAddConversation}
- type="link"
- style={styles.value.addBtn}
- icon={<PlusOutlined />}
- >
- New Conversation
- </Button>
- {/* 🌟 会话管理 */}
- <Conversations
- items={conversationsItems.value}
- style={styles.value.conversations}
- activeKey={activeKey.value}
- onActiveChange={onConversationClick}
- />
- </div>
- <div style={styles.value.chat}>
- {/* 🌟 消息列表 */}
- <Bubble.List
- items={items.value.length > 0 ? items.value : [{ content: placeholderNode.value, variant: 'borderless' }]}
- roles={roles}
- style={styles.value.messages}
- />
- {/* 🌟 提示词 */}
- <Prompts style={{ color: token.value.colorText }} items={senderPromptsItems} onItemClick={onPromptsItemClick} />
- {/* 🌟 输入框 */}
- <Sender
- value={content.value}
- header={senderHeader.value}
- onSubmit={onSubmit}
- onChange={(value) => content.value = value}
- prefix={attachmentsNode.value}
- loading={agentRequestLoading.value}
- style={styles.value.sender}
- />
- </div>
- </div>
- )
- });
- </script>
|