diff --git a/web/src/components/Chat/ChatContent.tsx b/web/src/components/Chat/ChatContent.tsx index eee7b526..2bcf479e 100644 --- a/web/src/components/Chat/ChatContent.tsx +++ b/web/src/components/Chat/ChatContent.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2025-12-10 16:46:17 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-31 15:01:53 + * @Last Modified time: 2026-04-02 16:05:01 */ import { type FC, useRef, useEffect, useState } from 'react' import clsx from 'clsx' @@ -124,6 +124,9 @@ const ChatContent: FC = ({ const handleDownload = (file: any) => { window.open(getFileUrl(file), '_blank') } + const onFormSubmit = (values: Record) => { + onSend?.(JSON.stringify(values)) + } return (
{data.length === 0 @@ -253,7 +256,7 @@ const ChatContent: FC = ({ {item.status &&
} {item.subContent && renderRuntime && renderRuntime(item, index)} {/* Render message content using Markdown component */} - + {item.meta_data?.suggested_questions && item.meta_data?.suggested_questions?.length > 0 && {item.meta_data?.suggested_questions?.map((question, idx) => ( diff --git a/web/src/components/Chat/types.ts b/web/src/components/Chat/types.ts index c125cfae..e7967bad 100644 --- a/web/src/components/Chat/types.ts +++ b/web/src/components/Chat/types.ts @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2025-12-10 16:45:54 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-31 15:01:46 + * @Last Modified time: 2026-04-02 16:05:08 */ import { type ReactNode } from 'react' diff --git a/web/src/components/Markdown/RbButton.tsx b/web/src/components/Markdown/RbButton.tsx index 48ea2ff6..ee100f9b 100644 --- a/web/src/components/Markdown/RbButton.tsx +++ b/web/src/components/Markdown/RbButton.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-02 15:16:10 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-02-25 14:02:17 + * @Last Modified time: 2026-04-02 15:41:16 */ /** * RbButton Component @@ -19,11 +19,11 @@ import { Button, type ButtonProps } from 'antd' /** Button component for rendering buttons in markdown */ -const RbButton: FC = (props) => { - const { children } = props; - +const RbButton: FC = ({ children, onClick, ...props }) => { + const size = (props['data-size'] || 'default') as ButtonProps['size'] + const type = (props['data-variant'] || 'default') as ButtonProps['type'] return ( - ) diff --git a/web/src/components/Markdown/index.tsx b/web/src/components/Markdown/index.tsx index 05d5c6c6..6b9dc12a 100644 --- a/web/src/components/Markdown/index.tsx +++ b/web/src/components/Markdown/index.tsx @@ -1,8 +1,8 @@ /* * @Author: ZhaoYing * @Date: 2026-02-02 15:17:31 - * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-02-02 15:17:31 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-04-02 16:06:03 */ /** * RbMarkdown Component @@ -22,7 +22,7 @@ * @component */ -import { useState, useRef, useEffect, type FC } from 'react' +import { useState, useRef, useEffect, type FC, createContext, useContext, useCallback } from 'react' import { Image, Input, Select, Form, Checkbox, Radio, ColorPicker, DatePicker, TimePicker, InputNumber, Slider } from 'antd' import ReactMarkdown from 'react-markdown' import RemarkGfm from 'remark-gfm' @@ -37,6 +37,13 @@ import AudioBlock from './AudioBlock' import Link from './Link' import RbButton from './RbButton' +/** Context for sharing form field values between form/input/button components */ +const FormContext = createContext<{ + values: Record; + setValue: (name: string, value: any) => void; + onSubmit?: (values: Record) => void; +} | null>(null) + /** Props interface for RbMarkdown component */ interface RbMarkdownProps { /** Markdown content to render */ @@ -49,10 +56,12 @@ interface RbMarkdownProps { onContentChange?: (content: string) => void; /** Additional CSS classes */ className?: string; + /** Callback when a form button is clicked, receives form field values */ + onFormSubmit?: (values: Record) => void; } -/** Custom component mappings for markdown elements */ -const components = { +/** Build components with onFormSubmit callback */ +const buildComponents = (onFormSubmit?: (values: Record) => void) => ({ h1: ({ children, ...props }: any) =>

{children}

, h2: ({ children, ...props }: any) =>

{children}

, h3: ({ children, ...props }: any) =>

{children}

, @@ -80,58 +89,93 @@ const components = { video: ({ src, ...props }: any) => , audio: ({ src, ...props }: any) => , a: ({ href, children, ...props }: any) => {children}, - button: ({ children }: any) => {[children]}, table: ({ children, ...props }: any) =>
{children}
, tr: ({ children, ...props }: any) => {children}, th: ({ children, ...props }: any) => {children}, td: ({ children, ...props }: any) => {children}, + button: ({ children, ...props }: any) => { + const ctx = useContext(FormContext) + return ctx?.onSubmit?.(ctx.values)}>{[children]} + }, + table: ({ children, ...props }: any) =>
{children}
, + tr: ({ children, ...props }: any) => {children}, + th: ({ children, ...props }: any) => {children}, + td: ({ children, ...props }: any) => {children}, input: ({ children, ...props }: any) => { + const ctx = useContext(FormContext) + const handleChange = useCallback((val: any) => { + if (props.name) ctx?.setValue(props.name, val) + }, [ctx, props.name]) switch (props.type) { case 'color': - return + return case 'time': - return + return case 'date': - return + return case 'datetime': case 'datetime-local': - return + return case 'week': - return + return case 'month': - return + return case 'number': - return + return case 'search': - return + return handleChange(e.target.value)} /> case 'range': - return + return case 'submit': case 'button': - return {[props.value || children]} + return ctx?.onSubmit?.(ctx.values)}>{[props.value || children]} case 'checkbox': - return {children} + return handleChange(e.target.checked)}>{children} case 'password': - return + return handleChange(e.target.value)} /> case 'radio': - return {children} + return handleChange(e.target.value)}>{children} + case 'select': { + const raw = props['data-options'] + const options = (typeof raw === 'string' ? JSON.parse(raw) : raw || []).map((v: string) => ({ label: v, value: v })) + return + return handleChange(e.target.value)} /> } }, - select: ({ children, ...props }: any) => , - textarea: ({ children, ...props }: any) => {children}, - form: ({ children, ...props }: any) =>
{children}
, - hr: (props: any) =>
-} + select: ({ children, ...props }: any) => { + const ctx = useContext(FormContext) + return + }, + textarea: ({ children, ...props }: any) => { + const ctx = useContext(FormContext) + return { if (props.name) ctx?.setValue(props.name, e.target.value) }}>{children} + }, + form: ({ children, ...props }: any) => { + const [values, setValues] = useState>({}) + const setValue = useCallback((name: string, value: any) => setValues(prev => ({ ...prev, [name]: value })), []) + return ( + +
{children}
+
+ ) + }, + label: ({ children, ...props }: any) => { + return + }, + hr: (props: any) =>
, +}) const RbMarkdown: FC = ({ content, showHtmlComments = false, editable = false, onContentChange, - className + className, + onFormSubmit, }) => { + const components = buildComponents(onFormSubmit) const [editContent, setEditContent] = useState(content) const textareaRef = useRef(null) @@ -207,8 +251,6 @@ const RbMarkdown: FC = ({ `} { const { t } = useTranslation(); @@ -51,18 +52,18 @@ const VersionCard: React.FC = () => { {introduction ? (<>
- {t('version.releaseDate')}: {introduction.releaseDate} | {t('version.name')}: {introduction.codeName} + {t('version.releaseDate')}: {formatDateTime(introduction.releaseDate, 'YYYY-MM-DD')} | {t('version.name')}: {introduction.codeName}

') }} /> {introduction.coreUpgrades?.map((item: string, index: number) => (

') }} /> ))}

diff --git a/web/src/views/Index/index.tsx b/web/src/views/Index/index.tsx index ee124c55..b10dc000 100644 --- a/web/src/views/Index/index.tsx +++ b/web/src/views/Index/index.tsx @@ -99,8 +99,8 @@ const Index = () => { return ( - - + +
@@ -123,14 +123,14 @@ const Index = () => { />
- - +
+
{/* 引导 */} - - +
+
); }