Merge pull request #807 from SuanmoSuanyangTechnology/feature/ui_upgrade_zy
Feature/UI upgrade zy
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2025-12-10 16:46:17
|
* @Date: 2025-12-10 16:46:17
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-04-07 21:29:59
|
* @Last Modified time: 2026-04-07 22:13:47
|
||||||
*/
|
*/
|
||||||
import { type FC, useRef, useEffect, useState } from 'react'
|
import { type FC, useRef, useEffect, useState } from 'react'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
@@ -284,7 +284,7 @@ const ChatContent: FC<ChatContentProps> = ({
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{/* Bottom label (such as timestamp, username, etc.) */}
|
{/* Bottom label (such as timestamp, username, etc.) */}
|
||||||
{labelPosition === 'bottom' && <Flex gap={16} align="center" justify={item.role === 'user' ? 'end' : 'start'}>
|
{(labelPosition === 'bottom' || item.meta_data?.audio_url) && <Flex gap={16} align="center" justify={item.role === 'user' ? 'end' : 'start'}>
|
||||||
{item.meta_data?.audio_url && <>
|
{item.meta_data?.audio_url && <>
|
||||||
{playingIndex !== item.meta_data?.audio_url && item.meta_data?.audio_status === 'pending'
|
{playingIndex !== item.meta_data?.audio_url && item.meta_data?.audio_status === 'pending'
|
||||||
? <Spin />
|
? <Spin />
|
||||||
@@ -299,9 +299,9 @@ const ChatContent: FC<ChatContentProps> = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</>}
|
</>}
|
||||||
<div className="rb:text-[#5B6167] rb:text-[12px] rb:leading-4 rb:font-regular">
|
{labelPosition === 'bottom' && <div className="rb:text-[#5B6167] rb:text-[12px] rb:leading-4 rb:font-regular">
|
||||||
{labelFormat(item)}
|
{labelFormat(item)}
|
||||||
</div>
|
</div>}
|
||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-02 15:17:31
|
* @Date: 2026-02-02 15:17:31
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-04-07 15:14:02
|
* @Last Modified time: 2026-04-07 21:56:00
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* RbMarkdown Component
|
* RbMarkdown Component
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
* @component
|
* @component
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useRef, useEffect, type FC, createContext, useContext, useCallback } from 'react'
|
import { useState, useRef, useEffect, type FC, createContext, useContext, useCallback, useMemo } from 'react'
|
||||||
import { Image, Input, Select, Form, Checkbox, Radio, ColorPicker, DatePicker, TimePicker, InputNumber, Slider } from 'antd'
|
import { Image, Input, Select, Form, Checkbox, Radio, ColorPicker, DatePicker, TimePicker, InputNumber, Slider } from 'antd'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import RemarkGfm from 'remark-gfm'
|
import RemarkGfm from 'remark-gfm'
|
||||||
@@ -44,6 +44,11 @@ const FormContext = createContext<{
|
|||||||
onSubmit?: (values: Record<string, any>) => void;
|
onSubmit?: (values: Record<string, any>) => void;
|
||||||
} | null>(null)
|
} | null>(null)
|
||||||
|
|
||||||
|
/** Stable form wrapper component — state lives in RbMarkdown, survives components object rebuilds */
|
||||||
|
const RbForm: FC<any> = ({ children, ...props }) => (
|
||||||
|
<Form {...props}>{children}</Form>
|
||||||
|
)
|
||||||
|
|
||||||
/** Props interface for RbMarkdown component */
|
/** Props interface for RbMarkdown component */
|
||||||
interface RbMarkdownProps {
|
interface RbMarkdownProps {
|
||||||
/** Markdown content to render */
|
/** Markdown content to render */
|
||||||
@@ -60,8 +65,8 @@ interface RbMarkdownProps {
|
|||||||
onFormSubmit?: (values: Record<string, any>) => void;
|
onFormSubmit?: (values: Record<string, any>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Build components with onFormSubmit callback */
|
/** Build stable components map — form submission handled via FormContext */
|
||||||
const buildComponents = (onFormSubmit?: (values: Record<string, any>) => void) => ({
|
const buildComponents = () => ({
|
||||||
h1: ({ children, ...props }: any) => <h1 className="rb:text-2xl rb:font-bold rb:mb-2" {...props}>{children}</h1>,
|
h1: ({ children, ...props }: any) => <h1 className="rb:text-2xl rb:font-bold rb:mb-2" {...props}>{children}</h1>,
|
||||||
h2: ({ children, ...props }: any) => <h2 className="rb:text-xl rb:font-bold rb:mb-2" {...props}>{children}</h2>,
|
h2: ({ children, ...props }: any) => <h2 className="rb:text-xl rb:font-bold rb:mb-2" {...props}>{children}</h2>,
|
||||||
h3: ({ children, ...props }: any) => <h3 className="rb:text-lg rb:font-bold rb:mb-2" {...props}>{children}</h3>,
|
h3: ({ children, ...props }: any) => <h3 className="rb:text-lg rb:font-bold rb:mb-2" {...props}>{children}</h3>,
|
||||||
@@ -95,49 +100,50 @@ const buildComponents = (onFormSubmit?: (values: Record<string, any>) => void) =
|
|||||||
td: ({ children, ...props }: any) => <td className="rb:border rb:border-[#EBEBEB] rb:px-2 rb:py-1 rb:text-left" {...props}>{children}</td>,
|
td: ({ children, ...props }: any) => <td className="rb:border rb:border-[#EBEBEB] rb:px-2 rb:py-1 rb:text-left" {...props}>{children}</td>,
|
||||||
button: ({ children, ...props }: any) => {
|
button: ({ children, ...props }: any) => {
|
||||||
const ctx = useContext(FormContext)
|
const ctx = useContext(FormContext)
|
||||||
return <RbButton {...props} onClick={() => ctx?.onSubmit?.(ctx.values)}>{[children]}</RbButton>
|
return <RbButton {...props} onClick={() => ctx?.onSubmit?.(ctx?.values ?? {})}>{[children]}</RbButton>
|
||||||
},
|
},
|
||||||
input: ({ children, ...props }: any) => {
|
input: ({ children, value, ...props }: any) => {
|
||||||
const ctx = useContext(FormContext)
|
const ctx = useContext(FormContext)
|
||||||
const handleChange = useCallback((val: any) => {
|
const handleChange = useCallback((val: any) => {
|
||||||
if (props.name) ctx?.setValue(props.name, val)
|
if (props.name) ctx?.setValue(props.name, val)
|
||||||
}, [ctx, props.name])
|
}, [ctx, props.name])
|
||||||
|
console.log('props', props)
|
||||||
switch (props.type) {
|
switch (props.type) {
|
||||||
case 'color':
|
case 'color':
|
||||||
return <ColorPicker className="rb:mb-4!" {...props} onChange={handleChange} />
|
return <ColorPicker className="rb:mb-4!" defaultValue={value} {...props} onChange={handleChange} />
|
||||||
case 'time':
|
case 'time':
|
||||||
return <TimePicker className="rb:mb-4!" {...props} onChange={handleChange} />
|
return <TimePicker className="rb:mb-4!" defaultValue={value} {...props} onChange={handleChange} />
|
||||||
case 'date':
|
case 'date':
|
||||||
return <DatePicker className="rb:mb-4!" {...props} onChange={handleChange} />
|
return <DatePicker className="rb:mb-4!" defaultValue={value} {...props} onChange={handleChange} />
|
||||||
case 'datetime':
|
case 'datetime':
|
||||||
case 'datetime-local':
|
case 'datetime-local':
|
||||||
return <DatePicker className="rb:mb-4!" showTime={true} {...props} onChange={handleChange} />
|
return <DatePicker className="rb:mb-4!" defaultValue={value} showTime={true} {...props} onChange={handleChange} />
|
||||||
case 'week':
|
case 'week':
|
||||||
return <DatePicker className="rb:mb-4!" picker="week" {...props} onChange={handleChange} />
|
return <DatePicker className="rb:mb-4!" defaultValue={value} picker="week" {...props} onChange={handleChange} />
|
||||||
case 'month':
|
case 'month':
|
||||||
return <DatePicker className="rb:mb-4!" picker="month" {...props} onChange={handleChange} />
|
return <DatePicker className="rb:mb-4!" defaultValue={value} picker="month" {...props} onChange={handleChange} />
|
||||||
case 'number':
|
case 'number':
|
||||||
return <InputNumber className="rb:mb-4!" {...props} onChange={handleChange} />
|
return <InputNumber className="rb:mb-4!" defaultValue={value} {...props} onChange={handleChange} />
|
||||||
case 'search':
|
case 'search':
|
||||||
return <Input.Search className="rb:mb-4!" {...props} onChange={(e) => handleChange(e.target.value)} />
|
return <Input.Search className="rb:mb-4!" defaultValue={value} {...props} onChange={(e) => handleChange(e.target.value)} />
|
||||||
case 'range':
|
case 'range':
|
||||||
return <Slider className="rb:mb-4!" {...props} onChange={handleChange} />
|
return <Slider className="rb:mb-4!" defaultValue={value} {...props} onChange={handleChange} />
|
||||||
case 'submit':
|
case 'submit':
|
||||||
case 'button':
|
case 'button':
|
||||||
return <RbButton className="rb:mb-4!" {...props} onClick={() => ctx?.onSubmit?.(ctx.values)}>{[props.value || children]}</RbButton>
|
return <RbButton className="rb:mb-4!" defaultValue={value} {...props} onClick={() => ctx?.onSubmit?.(ctx?.values ?? {})}>{[props.value || children]}</RbButton>
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
return <Checkbox className="rb:mb-4!" {...props} onChange={(e) => handleChange(e.target.checked)}>{children}</Checkbox>
|
return <Checkbox className="rb:mb-4!" defaultValue={value} {...props} onChange={(e) => handleChange(e.target.checked)}>{children}</Checkbox>
|
||||||
case 'password':
|
case 'password':
|
||||||
return <Input.Password className="rb:mb-4!" {...props} onChange={(e) => handleChange(e.target.value)} />
|
return <Input.Password className="rb:mb-4!" defaultValue={value} {...props} onChange={(e) => handleChange(e.target.value)} />
|
||||||
case 'radio':
|
case 'radio':
|
||||||
return <Radio className="rb:mb-4!" {...props} onChange={(e) => handleChange(e.target.value)}>{children}</Radio>
|
return <Radio className="rb:mb-4!" defaultValue={value} {...props} onChange={(e) => handleChange(e.target.value)}>{children}</Radio>
|
||||||
case 'select': {
|
case 'select': {
|
||||||
const raw = props['data-options']
|
const raw = props['data-options']
|
||||||
const options = (typeof raw === 'string' ? JSON.parse(raw) : raw || []).map((v: string) => ({ label: v, value: v }))
|
const options = (typeof raw === 'string' ? JSON.parse(raw) : raw || []).map((v: string) => ({ label: v, value: v }))
|
||||||
return <Select className="rb:mb-4! rb:w-full!" options={options} onChange={(val) => { if (props.name) ctx?.setValue(props.name, val) }} />
|
return <Select className="rb:mb-4! rb:w-full!" defaultValue={value} options={options} onChange={(val) => { if (props.name) ctx?.setValue(props.name, val) }} />
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return <Input className="rb:mb-4!" value={children} {...props} onChange={(e) => handleChange(e.target.value)} />
|
return <Input className="rb:mb-4!" defaultValue={value} {...props} onChange={(e) => handleChange(e.target.value)} />
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
select: ({ children, ...props }: any) => {
|
select: ({ children, ...props }: any) => {
|
||||||
@@ -148,15 +154,7 @@ const buildComponents = (onFormSubmit?: (values: Record<string, any>) => void) =
|
|||||||
const ctx = useContext(FormContext)
|
const ctx = useContext(FormContext)
|
||||||
return <Input.TextArea className="rb:mb-4!" {...props} onChange={(e) => { if (props.name) ctx?.setValue(props.name, e.target.value) }}>{children}</Input.TextArea>
|
return <Input.TextArea className="rb:mb-4!" {...props} onChange={(e) => { if (props.name) ctx?.setValue(props.name, e.target.value) }}>{children}</Input.TextArea>
|
||||||
},
|
},
|
||||||
form: ({ children, ...props }: any) => {
|
form: RbForm,
|
||||||
const [values, setValues] = useState<Record<string, any>>({})
|
|
||||||
const setValue = useCallback((name: string, value: any) => setValues(prev => ({ ...prev, [name]: value })), [])
|
|
||||||
return (
|
|
||||||
<FormContext.Provider value={{ values, setValue, onSubmit: onFormSubmit }}>
|
|
||||||
<Form {...props}>{children}</Form>
|
|
||||||
</FormContext.Provider>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label: ({ children, ...props }: any) => {
|
label: ({ children, ...props }: any) => {
|
||||||
return <label className="rb:block rb:font-medium rb:text-[#212332] rb:mb-2" {...props}>{children}</label>
|
return <label className="rb:block rb:font-medium rb:text-[#212332] rb:mb-2" {...props}>{children}</label>
|
||||||
},
|
},
|
||||||
@@ -171,7 +169,10 @@ const RbMarkdown: FC<RbMarkdownProps> = ({
|
|||||||
className,
|
className,
|
||||||
onFormSubmit,
|
onFormSubmit,
|
||||||
}) => {
|
}) => {
|
||||||
const components = buildComponents(onFormSubmit)
|
const [formValues, setFormValues] = useState<Record<string, any>>({})
|
||||||
|
const setValue = useCallback((name: string, value: any) => setFormValues(prev => ({ ...prev, [name]: value })), [])
|
||||||
|
const formCtx = useMemo(() => ({ values: formValues, setValue, onSubmit: onFormSubmit }), [formValues, setValue, onFormSubmit])
|
||||||
|
const components = useMemo(() => buildComponents(), [])
|
||||||
const [editContent, setEditContent] = useState(content)
|
const [editContent, setEditContent] = useState(content)
|
||||||
const textareaRef = useRef<any>(null)
|
const textareaRef = useRef<any>(null)
|
||||||
|
|
||||||
@@ -238,6 +239,7 @@ const RbMarkdown: FC<RbMarkdownProps> = ({
|
|||||||
|
|
||||||
/** Render markdown preview mode */
|
/** Render markdown preview mode */
|
||||||
return (
|
return (
|
||||||
|
<FormContext.Provider value={formCtx}>
|
||||||
<div className={`rb:relative ${className || ''}`} onKeyDown={handleKeyDown} tabIndex={0}>
|
<div className={`rb:relative ${className || ''}`} onKeyDown={handleKeyDown} tabIndex={0}>
|
||||||
<style>{`
|
<style>{`
|
||||||
.html-comment {
|
.html-comment {
|
||||||
@@ -274,6 +276,7 @@ const RbMarkdown: FC<RbMarkdownProps> = ({
|
|||||||
{processedContent}
|
{processedContent}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
</div>
|
</div>
|
||||||
|
</FormContext.Provider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default RbMarkdown
|
export default RbMarkdown
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-03-13 17:27:52
|
* @Date: 2026-03-13 17:27:52
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-04-07 21:31:19
|
* @Last Modified time: 2026-04-07 21:48:30
|
||||||
*/
|
*/
|
||||||
import { type FC, useState, useRef, useEffect } from 'react'
|
import { type FC, useState, useRef, useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@@ -95,7 +95,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getVariables()
|
getVariables()
|
||||||
}, [application, config])
|
}, [application, JSON.stringify(config)])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user