Merge pull request #711 from SuanmoSuanyangTechnology/feature/ui_upgrade_zy

Feature/UI upgrade zy
This commit is contained in:
yingzhao
2026-03-27 14:40:56 +08:00
committed by GitHub
72 changed files with 664 additions and 629 deletions

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2025-12-10 16:46:17
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-26 13:32:29
* @Last Modified time: 2026-03-27 14:17:38
*/
import { type FC, useRef, useEffect, useState } from 'react'
import clsx from 'clsx'
@@ -194,7 +194,10 @@ const ChatContent: FC<ChatContentProps> = ({
key={idx}
size="small"
className="rb:text-[12px]!"
onClick={() => window.open(`/knowledge/${citation.knowledge_id}/document/${citation.document_id}`, '_blank')}
onClick={() => {
const params = new URLSearchParams({ documentId: citation.document_id, parentId: citation.knowledge_id });
window.open(`/#/knowledge-base/${citation.knowledge_id}/DocumentDetails?${params}`, '_blank');
}}
>{citation.file_name}</Button>
))}
</div>}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-02 15:02:47
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-02 15:47:24
* @Last Modified time: 2026-03-26 14:58:24
*/
/**
* BodyWrapper Component

View File

@@ -3,7 +3,6 @@
align-items: center;
justify-content: space-between;
padding: 16px 24px 16px 20px;
height: 64px;
color: #212332;
z-index: 0;
font-size: 14px;

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-02 15:11:02
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-03 18:43:42
* @Last Modified time: 2026-03-26 15:01:02
*/
/**
* AuthLayout Component
@@ -54,14 +54,14 @@ const AuthLayout: FC = () => {
}, []);
return (
<Layout style={{ minHeight: '100vh' }}>
<Layout className="rb:min-h-screen!">
{/* Sidebar navigation */}
<Sider />
<Layout style={{maxHeight: '100vh', width: '100vh', overflowY: 'auto' }}>
<Layout className="rb:max-h-screen! rb:w-screen! rb:overflow-y-auto!">
{/* Header with breadcrumbs and user menu */}
<AppHeader />
{/* Main content area - renders child routes */}
<Content style={{ padding: '0 12px 20px 12px', zIndex: 0 }}>
<Content className="rb:px-3! rb:pb-3! rb:z-0! rb:flex-1!">
<Outlet />
</Content>
</Layout>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-02 15:11:43
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-05 14:57:08
* @Last Modified time: 2026-03-26 15:00:54
*/
/**
* AuthSpaceLayout Component
@@ -56,14 +56,14 @@ const AuthSpaceLayout: FC = () => {
}, []);
return (
<Layout style={{ minHeight: '100vh' }}>
<Layout className="rb:min-h-screen!">
{/* Sidebar navigation configured for space mode */}
<Sider source="space" />
<Layout style={{maxHeight: '100vh', width: '100vh', overflowY: 'auto' }}>
<Layout className="rb:max-h-screen! rb:w-screen! rb:overflow-y-auto!">
{/* Header with breadcrumbs and user menu configured for space mode */}
<AppHeader source="space" />
{/* Main content area for knowledge base pages - renders child routes */}
<Content style={{ padding: '0 12px 12px 12px', zIndex: 0, height: 'calc(100vh - 64px)', overflowY: 'auto' }}>
<Content className="rb:px-3! rb:pb-3! rb:z-0! rb:flex-1! rb:overflow-y-auto!">
<Outlet />
</Content>
</Layout>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-02 15:12:42
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-28 17:28:41
* @Last Modified time: 2026-03-26 15:36:25
*/
/**
* BasicAuthLayout Component
@@ -35,7 +35,7 @@ const BasicAuthLayout: FC = () => {
}, [getUserInfo]);
return (
<div className="rb:relative rb:h-full rb:w-full">
<div className="rb:relative rb:min-h-screen rb:w-screen">
{/* Render child routes without additional UI */}
<Outlet />
</div>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-10 11:08:27
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-24 11:19:48
* @Last Modified time: 2026-03-26 15:38:31
*/
/*
* PageHeader Component
@@ -43,7 +43,7 @@ const PageHeader: FC<ConfigHeaderProps> = ({
}) => {
return (
// Main header container: full width, 64px height, flex layout with space between
<Header className={`rb:w-full rb:h-16 rb:grid rb:grid-cols-${extra && centerContent ? '3' : ((extra && !centerContent) || (!extra && centerContent)) ? '2': 1} rb:gap-6 rb:px-4! rb:bg-white!`}>
<Header className={`rb:w-full rb:h-16! rb:grid rb:grid-cols-${extra && centerContent ? '3' : ((extra && !centerContent) || (!extra && centerContent)) ? '2': 1} rb:gap-6 rb:px-4! rb:bg-white!`}>
<Flex align="center" gap={8}>
{avatarUrl
? <img src={avatarUrl} alt={avatarUrl} className="rb:size-8 rb:rounded-lg rb:mr-2" />
@@ -58,7 +58,7 @@ const PageHeader: FC<ConfigHeaderProps> = ({
{operation}
</Flex>
{centerContent && <Flex align="center">
{centerContent && <Flex align="center" justify="center">
{centerContent}
</Flex>}
{/* Right section: Extra content (buttons, filters, etc.) */}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-02 15:18:19
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-19 20:47:34
* @Last Modified time: 2026-03-26 14:43:33
*/
/**
* PageScrollList Component
@@ -56,9 +56,10 @@ interface PageScrollListProps<T, Q = Record<string, unknown>> {
/** Additional CSS classes */
className?: string;
needLoading?: boolean;
heightClass?: string;
}
const heightClass = 'rb:h-[calc(100vh-124px)]!';
const defaultHeightClass = 'rb:h-[calc(100vh-116px)]!';
/** Infinite scroll list component with pagination support */
const PageScrollList = forwardRef(<T, Q = Record<string, unknown>>({
@@ -68,6 +69,7 @@ const PageScrollList = forwardRef(<T, Q = Record<string, unknown>>({
column = 4,
className = '',
needLoading = true,
heightClass,
}: PageScrollListProps<T, Q>, ref: React.Ref<PageScrollListRef>) => {
/** Expose refresh method to parent component */
useImperativeHandle(ref, () => ({
@@ -140,13 +142,13 @@ const PageScrollList = forwardRef(<T, Q = Record<string, unknown>>({
<div
ref={scrollRef}
id="scrollableDiv"
className={`rb:overflow-y-auto rb:overflow-x-hidden ${heightClass} ${className}`}
className={`rb:overflow-y-auto rb:overflow-x-hidden ${heightClass || defaultHeightClass} ${className}`}
>
<InfiniteScroll
dataLength={data.length}
next={() => loadMoreData()}
hasMore={hasMore}
loader={loading && needLoading ? <PageLoading className={heightClass} /> : false}
loader={loading && needLoading ? <PageLoading className={heightClass || defaultHeightClass} /> : false}
// endMessage={<Divider plain>It is all, nothing more 🤐</Divider>}
scrollableTarget="scrollableDiv"
className='rb:h-full!'
@@ -162,7 +164,7 @@ const PageScrollList = forwardRef(<T, Q = Record<string, unknown>>({
</Col>
))}
</Row>
) : !loading ? <PageEmpty className={heightClass} /> : null}
) : !loading ? <PageEmpty className={heightClass || defaultHeightClass} /> : null}
</InfiniteScroll>
</div>
</>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-02 15:29:46
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 17:11:55
* @Last Modified time: 2026-03-26 14:52:23
*/
/**
* RbTable Component
@@ -199,7 +199,7 @@ const RbTable = forwardRef(<T = Record<string, unknown>, Q = Record<string, unkn
if (scrollY !== undefined) {
config.y = scrollY;
} else if (isScroll) {
config.y = 'calc(100vh - 232px)';
config.y = 'calc(100vh - 224px)';
}
return Object.keys(config).length > 0 ? config : undefined;

View File

@@ -32,7 +32,7 @@ export const lightTheme: ThemeConfig = {
bodyBg: '#EEEFF4',
siderBg: '#FAFCFF',
headerPadding: '0 24px 0 20px',
headerHeight: 64,
headerHeight: 56,
headerColor: '#212332',
},
Menu: {

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:29:21
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-26 12:13:33
* @Last Modified time: 2026-03-27 13:46:18
*/
import { useEffect, useRef, useState, forwardRef, useImperativeHandle, useMemo } from 'react';
import { useTranslation } from 'react-i18next'
@@ -44,6 +44,14 @@ import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
import DescWrapper from '@/components/FormItem/DescWrapper'
import FeaturesConfig from './components/FeaturesConfig'
import { getListLogoUrl } from '@/views/ModelManagement/utils';
import type { ChatItem } from '@/components/Chat/types'
export const replaceVariables = (statement: string, variables: Variable[]) => {
return statement.replace(/\{\{([^}]+)\}\}/g, (match, name) => {
const v = variables.find(item => item.name === name)
return v?.value != null && v.value !== '' ? String(v.value) : match
})
}
/**
* Agent configuration component
@@ -148,8 +156,9 @@ const Agent = forwardRef<AgentRef, { onFeaturesLoad?: (features: FeaturesConfigF
model_parameters: {...rest}
})
if (default_model_config_id === values?.default_model_config_id) {
const label = defaultModel?.id === default_model_config_id && defaultModel?.name ? defaultModel.name : vo.label || ''
setChatList([{
label: defaultModel?.id === default_model_config_id && defaultModel?.name ? defaultModel.name : vo.label || '',
label: label,
model_config_id: default_model_config_id || '',
model_parameters: {...rest},
list: []
@@ -331,28 +340,13 @@ const Agent = forwardRef<AgentRef, { onFeaturesLoad?: (features: FeaturesConfigF
const handleOpenVariableConfig = () => {
chatVariableConfigModalRef.current?.handleOpen(chatVariables)
}
/**
* Save chat variable configuration
* @param values - Variable values
*/
const handleSaveChatVariable = (variables: Variable[]) => {
setChatVariables(variables)
const opening_statement = form.getFieldValue(['features', 'opening_statement'])
if (opening_statement?.statement && opening_statement?.statement.trim() !== '') {
const statement = opening_statement.statement as string
const replacedContent = statement.replace(/\{\{([^}]+)\}\}/g, (match, name) => {
const v = variables.find(item => item.name === name)
return v?.value != null && v.value !== '' ? String(v.value) : match
})
setChatList(prev => prev.map(item => {
const list = [...(item.list || [])]
if (list.length > 0 && list[0].role === 'assistant') {
list[0] = { ...list[0], content: replacedContent }
}
return { ...item, list }
}))
}
}
useEffect(() => {
setChatVariables(values?.variables || [])
@@ -360,43 +354,55 @@ const Agent = forwardRef<AgentRef, { onFeaturesLoad?: (features: FeaturesConfigF
const handleSaveFeaturesConfig = (value: FeaturesConfigForm) => {
form.setFieldValue('features', value)
if (value?.opening_statement?.statement && value?.opening_statement?.statement.trim() !== '') {
setChatList(prev => (prev.map(item => {
const firstMsg = item.list?.[0]
if (firstMsg?.role === 'assistant') {
firstMsg.meta_data = {
suggested_questions: value.opening_statement?.suggested_questions || []
}
return item
} else {
return {
...item,
list: [{
role: 'assistant',
content: value.opening_statement?.statement,
meta_data: {
suggested_questions: value.opening_statement?.suggested_questions || []
}
}, ...(item.list || [])]
}
}
})))
}
}
const modelLogo = useMemo(() => {
return defaultModel?.name && getListLogoUrl(defaultModel.provider, defaultModel.logo as string)
}, [defaultModel])
useEffect(() => {
const opening_statement = form.getFieldValue(['features', 'opening_statement'])
console.log('opening_statement', opening_statement, defaultModel, chatList)
if (opening_statement?.enabled && opening_statement?.statement && opening_statement?.statement.trim() !== '') {
const assistantMsg: ChatItem = {
role: 'assistant',
content: replaceVariables(opening_statement.statement, chatVariables),
meta_data: {
suggested_questions: opening_statement?.suggested_questions
}
}
setChatList(prev => {
if (prev.length === 0 && !defaultModel) return prev
if (defaultModel && prev.length === 1) {
return [{
label: defaultModel.name,
model_config_id: defaultModel.id,
model_parameters: defaultModel.config as unknown as ModelConfig,
list: [assistantMsg]
}]
}
return prev.map(vo => {
if (vo.list?.length === 0) {
return { ...vo, list: [assistantMsg] }
} else if (vo.list && vo.list[0].role === 'assistant') {
return { ...vo, list: [assistantMsg, ...vo.list.slice(1)] }
} else {
return { ...vo, list: [assistantMsg, ...(vo.list || [])] }
}
})
})
}
}, [defaultModel, chatList.length, form.getFieldValue(['features', 'opening_statement']), chatVariables])
console.log('agent values', values)
return (
<>
{loading && <Spin fullscreen></Spin>}
<Row className="rb:h-[calc(100vh-88px)]" gutter={12}>
<Col span={12} className="rb:h-full rb:overflow-y-auto">
<Row className="rb:h-full!" gutter={12}>
<Col span={12} className="rb:h-full!">
<Form form={form}>
<Flex gap={16} vertical>
<Flex gap={12} vertical>
<Flex align="center" justify="space-between" className="rb:p-3! rb:bg-white rb:rounded-xl">
<Button type="primary" ghost onClick={handleModelConfig} className="rb:group">
{modelLogo
@@ -417,89 +423,92 @@ const Agent = forwardRef<AgentRef, { onFeaturesLoad?: (features: FeaturesConfigF
</Button>
</Space>
</Flex>
<Form.Item name="default_model_config_id" hidden noStyle></Form.Item>
<Form.Item name="capability" hidden noStyle></Form.Item>
<Form.Item name="model_parameters" hidden noStyle></Form.Item>
<Form.Item name="features" hidden noStyle></Form.Item>
<Card
title={t('application.promptConfiguration')}
extra={
<Space
size={1}
className="rb:px-2 rb:h-5.5 rb:rounded-md rb:cursor-pointer rb:border rb:border-[rgba(21,94,239,0.3)] rb:text-[#155EEF]"
onClick={handlePrompt}
>
<div className="rb:size-5 rb:bg-cover rb:bg-[url('@/assets/images/application/aiPrompt.png')]"></div>
<span className="rb:font-[PingFangSC, PingFang_SC]!">{t('application.aiPrompt')}</span>
</Space>
}
>
<div className="rb:leading-4.5 rb:text-[12px] rb:mb-2">
<span className="rb:font-medium">{t('application.configuration')}</span>
<span className="rb:font-regular rb:text-[#5B6167]"> ({t('application.configurationDesc')})</span>
</div>
<Form.Item name="system_prompt" className="rb:mb-0!">
<Input.TextArea
placeholder={t('application.promptPlaceholder')}
styles={{
textarea: {
minHeight: '200px',
borderRadius: '8px',
padding: '12px'
},
}}
/>
</Form.Item>
</Card>
<Flex gap={12} vertical className="rb:h-[calc(100vh-156px)]! rb:overflow-y-auto!">
<Form.Item name="default_model_config_id" hidden noStyle></Form.Item>
<Form.Item name="capability" hidden noStyle></Form.Item>
<Form.Item name="model_parameters" hidden noStyle></Form.Item>
<Form.Item name="features" hidden noStyle></Form.Item>
<Card
title={t('application.promptConfiguration')}
extra={
<Space
size={1}
className="rb:px-2 rb:h-5.5 rb:rounded-md rb:cursor-pointer rb:border rb:border-[rgba(21,94,239,0.3)] rb:text-[#155EEF]"
onClick={handlePrompt}
>
<div className="rb:size-5 rb:bg-cover rb:bg-[url('@/assets/images/application/aiPrompt.png')]"></div>
<span className="rb:font-[PingFangSC, PingFang_SC]!">{t('application.aiPrompt')}</span>
</Space>
}
>
<div className="rb:leading-4.5 rb:text-[12px] rb:mb-2">
<span className="rb:font-medium">{t('application.configuration')}</span>
<span className="rb:font-regular rb:text-[#5B6167]"> ({t('application.configurationDesc')})</span>
</div>
<Form.Item name="system_prompt" className="rb:mb-0!">
<Input.TextArea
placeholder={t('application.promptPlaceholder')}
styles={{
textarea: {
minHeight: '200px',
borderRadius: '8px',
padding: '12px'
},
}}
/>
</Form.Item>
</Card>
<Form.Item name="knowledge_retrieval" noStyle>
<Knowledge />
</Form.Item>
{/* Memory Configuration */}
<Card title={t('application.memoryConfiguration')}>
<Flex gap={16} vertical className="rb:bg-[#FAFAFA] rb:rounded-xl rb:p-3!">
<SwitchFormItem
title={t('application.dialogueHistoricalMemory')}
name={['memory', 'enabled']}
desc={t('application.dialogueHistoricalMemoryDesc')}
/>
<Form.Item
name={['memory', 'memory_config_id']}
label={t('application.selectMemoryContent')}
extra={<DescWrapper desc={t('application.selectMemoryContentDesc')} className="rb:mt-1" />}
layout="vertical"
className="rb:mb-0!"
>
<CustomSelect
placeholder={t('common.pleaseSelect')}
url={memoryConfigListUrl}
hasAll={false}
valueKey='config_id'
labelKey="config_name"
disabled={!values?.memory?.enabled}
{/* Memory Configuration */}
<Card title={t('application.memoryConfiguration')}>
<Flex gap={16} vertical className="rb:bg-[#FAFAFA] rb:rounded-xl rb:p-3!">
<SwitchFormItem
title={t('application.dialogueHistoricalMemory')}
name={['memory', 'enabled']}
desc={t('application.dialogueHistoricalMemoryDesc')}
/>
</Form.Item>
</Flex>
</Card>
<Form.Item
name={['memory', 'memory_config_id']}
label={t('application.selectMemoryContent')}
extra={<DescWrapper desc={t('application.selectMemoryContentDesc')} className="rb:mt-1" />}
layout="vertical"
className="rb:mb-0!"
>
<CustomSelect
placeholder={t('common.pleaseSelect')}
url={memoryConfigListUrl}
hasAll={false}
valueKey='config_id'
labelKey="config_name"
disabled={!values?.memory?.enabled}
/>
</Form.Item>
</Flex>
</Card>
<Form.Item name="variables" noStyle>
<VariableList />
</Form.Item>
<Form.Item name="variables" noStyle>
<VariableList />
</Form.Item>
<Form.Item name="skills" noStyle>
<SkillList />
</Form.Item>
<Form.Item name="skills" noStyle>
<SkillList />
</Form.Item>
{/* Tool Configuration */}
<Form.Item name="tools" noStyle>
<ToolList />
</Form.Item>
{/* Tool Configuration */}
<Form.Item name="tools" noStyle>
<ToolList />
</Form.Item>
</Flex>
</Flex>
</Form>
</Col>
<Col span={12} className="rb:h-full rb:overflow-y-hidden">
<Col span={12} className="rb:h-full! rb:overflow-y-hidden">
<RbCard
title={t('application.debuggingAndPreview')}
extra={
@@ -513,7 +522,7 @@ const Agent = forwardRef<AgentRef, { onFeaturesLoad?: (features: FeaturesConfigF
headerType="borderless"
headerClassName="rb:h-[56px]! rb:leading-[22px]!"
titleClassName="rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-4! rb:pt-0!"
bodyClassName="rb:p-4! rb:pt-0! rb:h-[calc(100%-56px)]!"
className="rb:h-full!"
>
<Chat

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:29:29
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 11:18:09
* @Last Modified time: 2026-03-26 15:31:36
*/
import { type FC, useState, useRef, useEffect } from 'react';
import clsx from 'clsx';
@@ -124,7 +124,7 @@ const Api: FC<{ application: Application | null }> = ({ application }) => {
// Calculate total requests across all API keys
const totalRequests = apiKeyList.reduce((total, item) => total + item.total_requests, 0);
return (
<div className="rb:w-250 rb:mx-auto">
<div className="rb:w-250 rb:mx-auto rb:max-h-[calc(100vh-88px)]! rb:overflow-y-auto">
<Flex gap={20} vertical>
<RbCard
title={() => (<Flex align="center">

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:29:33
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-20 15:16:25
* @Last Modified time: 2026-03-27 11:51:34
*/
import { useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react'
import { useTranslation } from 'react-i18next'
@@ -197,33 +197,34 @@ const Cluster = forwardRef<ClusterRef, { onFeaturesLoad?: (features: FeaturesCon
return (
<>
{loading && <Spin fullscreen></Spin>}
<Row className="rb:h-[calc(100vh-89px)]" gutter={12}>
<Col span={12} className="rb:h-full rb:overflow-x-auto rb:border-r rb:border-[#DFE4ED]">
<Row className="rb:h-full!" gutter={12}>
<Col span={12}>
<Form form={form} layout="vertical">
<Flex gap={16} vertical>
<Flex gap={12} vertical>
<Flex align="center" justify="end" className="rb:p-3! rb:bg-white rb:rounded-xl">
{/* <FeaturesConfig value={values?.features as FeaturesConfigForm} refresh={handleSaveFeaturesConfig} /> */}
<Button type="primary" onClick={() => handleSave()}>
{t('common.save')}
</Button>
</Flex>
<Form.Item name="features" hidden noStyle></Form.Item>
<Card title={t('application.collaboration')}>
<Form.Item
name="orchestration_mode"
noStyle
>
<RadioGroupCard
options={['supervisor', 'collaboration'].map((type) => ({
value: type,
label: t(`application.${type}`),
labelDesc: t(`application.${type}Desc`),
}))}
allowClear={false}
block={true}
/>
</Form.Item>
</Card>
<Flex gap={12} vertical className="rb:h-[calc(100vh-158px)]! rb:overflow-y-auto!">
<Form.Item name="features" hidden noStyle></Form.Item>
<Card title={t('application.collaboration')}>
<Form.Item
name="orchestration_mode"
noStyle
>
<RadioGroupCard
options={['supervisor', 'collaboration'].map((type) => ({
value: type,
label: t(`application.${type}`),
labelDesc: t(`application.${type}Desc`),
}))}
allowClear={false}
block={true}
/>
</Form.Item>
</Card>
<Card
title={<>
@@ -289,45 +290,46 @@ const Cluster = forwardRef<ClusterRef, { onFeaturesLoad?: (features: FeaturesCon
>{t('application.modelConfig')}</Button>
</Form.Item>
</Flex>
</Form.Item>
<Form.Item
name={['execution_config', "sub_agent_execution_mode"]}
label={<span className="rb:text-[#5B6167]">{t('application.orchestrationMode')}</span>}
className="rb:mb-4!"
>
<Select
options={['sequential', 'parallel'].map((type) => ({
value: type,
label: t(`application.${type}`),
}))}
placeholder={t('common.pleaseSelect')}
/>
</Form.Item>
<Form.Item
name="aggregation_strategy"
label={<span className="rb:text-[#5B6167]">{t('application.aggregationStrategy')}</span>}
className="rb:mb-0!"
>
<Select
options={['merge', 'vote', 'priority'].map((type) => ({
value: type,
label: t(`application.${type}`),
}))}
placeholder={t('common.pleaseSelect')}
/>
</Form.Item>
</Card>}
</Form.Item>
<Form.Item
name={['execution_config', "sub_agent_execution_mode"]}
label={<span className="rb:text-[#5B6167]">{t('application.orchestrationMode')}</span>}
className="rb:mb-4!"
>
<Select
options={['sequential', 'parallel'].map((type) => ({
value: type,
label: t(`application.${type}`),
}))}
placeholder={t('common.pleaseSelect')}
/>
</Form.Item>
<Form.Item
name="aggregation_strategy"
label={<span className="rb:text-[#5B6167]">{t('application.aggregationStrategy')}</span>}
className="rb:mb-0!"
>
<Select
options={['merge', 'vote', 'priority'].map((type) => ({
value: type,
label: t(`application.${type}`),
}))}
placeholder={t('common.pleaseSelect')}
/>
</Form.Item>
</Card>}
</Flex>
</Flex>
</Form>
</Col>
<Col span={12} className="rb:h-full rb:overflow-y-hidden">
<Col span={12} className="rb:h-full! rb:overflow-y-hidden">
<RbCard
title={t('application.debuggingAndPreview')}
headerType="borderless"
headerClassName="rb:h-[56px]! rb:leading-[22px]!"
titleClassName="rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-4! rb:pt-0!"
className="rb:h-full"
bodyClassName="rb:p-4! rb:pt-0! rb:h-[calc(100%-56px)]!"
className="rb:h-full!"
>
<Chat
data={data as Config}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:29:41
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-24 16:43:10
* @Last Modified time: 2026-03-26 15:24:41
*/
import { type FC, useState, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
@@ -30,7 +30,7 @@ const tagColors: Record<Release['tagKey'], TagProps['color']> = {
history: 'default',
}
const heightClass = 'rb:h-[calc(100vh-88px)]'
const heightClass = 'rb:max-h-[calc(100vh-140px)]'
/**
* Release page component
* Manages application version releases, rollbacks, and version history
@@ -77,51 +77,55 @@ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refres
}
return (
<Flex gap={12}>
<div className={`rb:overflow-y-auto rb:w-101 rb:flex-[0_0_auto] ${heightClass}`}>
<div className="rb:w-101 rb:h-full">
<Flex gap={12} vertical>
<div className="rb:px-1">
<div className="rb:text-[16px] rb:leading-5.5 rb:font-medium">{t('application.versionList')}</div>
<div className="rb:text-[12px] rb:text-[#5B6167] rb:leading-4.5">{t('application.versionListDesc')}</div>
</div>
{releaseList.length === 0
? <Empty />
: selectedVersion && releaseList.map((version, index) => {
const tagKey = version.id === data.current_release_id && index === 0
? 'current'
: version.id === data.current_release_id
? 'rolledBack' : 'history'
return (
<RbCard
key={version.version}
title={<>
{version.version_name && version.version_name[0].toLocaleLowerCase() === 'v' ? version.version_name : version.version_name ? `v${version.version_name}` : `v${version.version}`}
{tagKey && <Tag color={tagColors[tagKey]} className="rb:ml-2">
{tagKey}
</Tag>}
</>}
className={clsx("rb:hover:shadow-[0px_2px_8px_0px_rgba(0,0,0,0.2)]! rb:cursor-pointer rb:bg-white", {
'rb:border-[#171719]!': version.id === selectedVersion.id,
'rb:border-[#DFE4ED] ': version.id !== selectedVersion.id
})}
headerType="borderless"
onClick={() => setSelectedVersion(version)}
>
<div className="rb:leading-5 rb:line-clamp-2 rb:overflow-hidden rb:text-ellipsis rb:whitespace-nowrap">
<Markdown content={version.release_notes} />
</div>
<div className="rb:mt-4 rb:text-[12px] rb:text-[#5B6167] rb:leading-4.5">
{t('application.publishedOn')} {formatDateTime(version.published_at, 'YYYY-MM-DD HH:mm:ss')}
</div>
<div className="rb:text-[12px] rb:text-[#5B6167] rb:leading-4.5">
{t('application.publisher')}: {version.publisher_name}
</div>
</RbCard>
)
})
}
<div className={`${heightClass} rb:overflow-y-auto`}>
{releaseList.length === 0
? <Empty />
: <Flex gap={12} vertical>
{selectedVersion && releaseList.map((version, index) => {
const tagKey = version.id === data.current_release_id && index === 0
? 'current'
: version.id === data.current_release_id
? 'rolledBack' : 'history'
return (
<RbCard
key={version.version}
title={<>
{version.version_name && version.version_name[0].toLocaleLowerCase() === 'v' ? version.version_name : version.version_name ? `v${version.version_name}` : `v${version.version}`}
{tagKey && <Tag color={tagColors[tagKey]} className="rb:ml-2">
{tagKey}
</Tag>}
</>}
className={clsx("rb:hover:shadow-[0px_2px_8px_0px_rgba(0,0,0,0.2)]! rb:cursor-pointer rb:bg-white", {
'rb:border-[#171719]!': version.id === selectedVersion.id,
'rb:border-[#DFE4ED] ': version.id !== selectedVersion.id
})}
headerType="borderless"
onClick={() => setSelectedVersion(version)}
>
<div className="rb:leading-5 rb:line-clamp-2 rb:overflow-hidden rb:text-ellipsis rb:whitespace-nowrap">
<Markdown content={version.release_notes} />
</div>
<div className="rb:mt-4 rb:text-[12px] rb:text-[#5B6167] rb:leading-4.5">
{t('application.publishedOn')} {formatDateTime(version.published_at, 'YYYY-MM-DD HH:mm:ss')}
</div>
<div className="rb:text-[12px] rb:text-[#5B6167] rb:leading-4.5">
{t('application.publisher')}: {version.publisher_name}
</div>
</RbCard>
)
})}
</Flex>
}
</div>
</Flex>
</div>
<div className={`rb:overflow-y-auto rb:flex-[1_1_auto] ${heightClass}`}>
<div className="rb:overflow-y-auto rb:flex-1">
<Form layout="vertical">
<Flex align="center" className={clsx("rb:leading-6.5! rb:text-[18px] rb:font-medium rb:mb-4.75!", {
'rb:justify-between': selectedVersion,
@@ -140,7 +144,7 @@ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refres
</Space>
</Flex>
{selectedVersion &&
<Flex gap={16} vertical>
<Flex gap={16} vertical className={`${heightClass} rb:overflow-y-auto`}>
<RbCard
title={t('application.VersionInformation')}
headerType="borderless"

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-03-13 17:27:52
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-26 13:43:02
* @Last Modified time: 2026-03-26 15:35:13
*/
import { type FC, useState, useRef, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
@@ -531,11 +531,11 @@ const TestChat: FC<TestChatProps> = ({
}
return (
<div className="rb:w-250 rb:p-3 rb:mx-auto">
<div className="rb:w-250 rb:mx-auto rb:h-full">
<RbCard
title={t('application.test')}
headerClassName="rb:min-h-[56px]!"
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
bodyClassName="rb:h-[calc(100%-56px)]! rb:overflow-y-auto rb:px-3! rb:py-0!"
>
<Chat

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:27:56
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-26 14:03:01
* @Last Modified time: 2026-03-27 14:24:47
*/
/**
* Copy Application Modal
@@ -74,16 +74,16 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
}
const formatFileTypeOptions = (fu: FeaturesConfigForm['file_upload']) => {
let options = [{ type: 'document', enabled: fu.document_enabled, maxSize: fu.document_max_size_mb }]
let options = fu.document_enabled ? [{ type: 'document', enabled: fu.document_enabled, maxSize: fu.document_max_size_mb }] : []
if (!capability) return options
if (capability.includes('vision')) {
if ((capability.includes('vision') || source === 'workflow') && fu.image_enabled) {
options.push({ type: 'image', enabled: fu.image_enabled, maxSize: fu.image_max_size_mb })
}
if (capability.includes('audio')) {
if ((capability.includes('audio') || source === 'workflow') && fu.audio_enabled) {
options.push({ type: 'audio', enabled: fu.audio_enabled, maxSize: fu.audio_max_size_mb })
}
if (capability.includes('video')) {
if ((capability.includes('video') || source === 'workflow') && fu.video_enabled) {
options.push({ type: 'video', enabled: fu.video_enabled, maxSize: fu.video_max_size_mb })
}
return options.filter(item => item.enabled)
@@ -201,6 +201,7 @@ const FeaturesConfigModal = forwardRef<FeaturesConfigModalRef, FeaturesConfigMod
ref={fileUploadSettingModalRef}
onSave={handleSaveSettings}
capability={capability}
source={source}
/>
<OpenStatementSettingModal
ref={openStatementSettingModalRef}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-03-05
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-24 11:00:14
* @Last Modified time: 2026-03-27 14:02:40
*/
import { forwardRef, useImperativeHandle, useState, useMemo } from 'react';
import { Form, InputNumber, Flex, Switch, Row, Col, Radio } from 'antd';
@@ -12,6 +12,7 @@ import clsx from 'clsx';
import RbModal from '@/components/RbModal';
import type { FeaturesConfigForm } from '../../types'
import type { Capability } from '@/views/ModelManagement/types'
import type { Application } from '@/views/ApplicationManagement/types';
type FileUpload = Omit<FeaturesConfigForm['file_upload'], 'settings'>
@@ -23,6 +24,7 @@ interface FileUploadSettingModalRef {
interface FileUploadSettingModalProps {
onSave: (values: FileUpload) => void;
capability?: Capability[];
source?: Application['type']
}
const documentType = {
type: 'document',
@@ -108,6 +110,7 @@ const defaultValues: FileUpload = {
const FileUploadSettingModal = forwardRef<FileUploadSettingModalRef, FileUploadSettingModalProps>(({
onSave,
capability,
source,
}, ref) => {
const { t } = useTranslation();
const [visible, setVisible] = useState(false);
@@ -149,6 +152,14 @@ const FileUploadSettingModal = forwardRef<FileUploadSettingModalRef, FileUploadS
}));
const fileTypeOptions = useMemo(() => {
if (source === 'workflow') {
return [
documentType,
imageType,
audioType,
videoType,
]
}
let options = [documentType]
if (!capability) return options
if (capability.includes('vision')) options = [...options, imageType]

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-03-05
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-26 14:12:11
* @Last Modified time: 2026-03-27 14:38:28
*/
import { forwardRef, useImperativeHandle, useState } from 'react';
import { Button, Form, Input, Flex, App } from 'antd';
@@ -44,12 +44,13 @@ const OpenStatementSettingModal = forwardRef<OpenStatementSettingModalRef, OpenS
const handleSave = async () => {
form.validateFields().then(values => {
const { suggested_questions, ...rest } = values
const filterSuggestedQuestions = suggested_questions.filter(vo => vo && vo.trim() !== '' && vo !== null)
if (values?.enabled && values?.statement && values?.statement?.trim() !== '') {
const usedVars = [...new Set([...values.statement?.matchAll(/\{\{(\w+)\}\}/g)].map(m => m[1]))]
const validNames = new Set(chatVariables.map(v => v.name))
const invalid = usedVars.filter(v => !validNames.has(v))
console.log('invalid', invalid)
if (invalid.length > 0) {
modal.confirm({
title: t('application.invalidVariablesTitle'),
@@ -57,14 +58,26 @@ const OpenStatementSettingModal = forwardRef<OpenStatementSettingModalRef, OpenS
okText: t('common.confirm'),
cancelText: t('common.cancel'),
onOk: () => {
onSave(values);
onSave({
...rest,
suggested_questions: filterSuggestedQuestions
});
handleClose();
},
})
} else {
onSave(values);
onSave({
...rest,
suggested_questions: filterSuggestedQuestions
});
handleClose();
}
} else {
onSave({
...rest,
suggested_questions: filterSuggestedQuestions
});
handleClose();
}
});
};

View File

@@ -139,6 +139,7 @@ const ModelConfigModal = forwardRef<ModelConfigModalRef, ModelConfigModalProps>(
>
{source !== 'multi_agent' &&
<ModelSelect
params={{type: 'llm,chat'}}
placeholder={t('common.pleaseSelect')}
onChange={handleChange}
/>

View File

@@ -2,10 +2,11 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:29:37
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-24 15:59:47
* @Last Modified time: 2026-03-26 15:37:18
*/
import React, { useEffect, useState, useRef } from 'react';
import { useParams } from 'react-router-dom';
import { Flex } from 'antd'
import ConfigHeader from './components/ConfigHeader'
import type { AgentRef, ClusterRef, WorkflowRef, Config } from './types'
@@ -108,7 +109,7 @@ const ApplicationConfig: React.FC = () => {
}
return (
<>
<Flex vertical className="rb:h-screen!">
<ConfigHeader
activeTab={activeTab}
handleChangeTab={handleChangeTab}
@@ -119,7 +120,7 @@ const ApplicationConfig: React.FC = () => {
features={features}
onFeaturesChange={setFeatures}
/>
<div className="rb:p-3 rb:max-h-[calc(100vh-65px)] rb:overflow-auto">
<div className="rb:p-3 rb:flex-1 rb:overflow-auto">
{activeTab === 'arrangement' && application?.type === 'agent' && <Agent ref={agentRef} onFeaturesLoad={setFeatures} />}
{activeTab === 'arrangement' && application?.type === 'multi_agent' && <Cluster ref={clusterRef} onFeaturesLoad={setFeatures} />}
{activeTab === 'arrangement' && application?.type === 'workflow' && <Workflow ref={workflowRef} onFeaturesLoad={setFeatures} />}
@@ -129,7 +130,7 @@ const ApplicationConfig: React.FC = () => {
{activeTab === 'test' && <TestChat application={application} config={config} />}
{activeTab === 'log' && <Logs />}
</div>
</>
</Flex>
);
};

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:34:12
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 11:37:51
* @Last Modified time: 2026-03-26 14:39:18
*/
import React, { useState, useEffect, useMemo, type MouseEvent } from 'react';
import { useTranslation } from 'react-i18next';
@@ -81,7 +81,7 @@ const MySharing: React.FC = () => {
}
return (
<Flex vertical gap={12} className="rb:h-[calc(100vh-148px)]! rb:overflow-y-auto!">
<Flex vertical gap={12} className="rb:max-h-[calc(100%-48px)]! rb:overflow-y-auto!">
<BodyWrapper loading={false} empty={data.length === 0}>
{grouped.map(({ workspace, items }) => (
<Collapse

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:58:03
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-26 13:35:42
* @Last Modified time: 2026-03-27 14:28:19
*/
/**
* Conversation Page
@@ -30,8 +30,10 @@ import { type SSEMessage } from '@/utils/stream'
import { shareFileUploadUrlWithoutApiPrefix } from '@/api/fileStorage'
import ChatToolbar, { type ChatToolbarRef } from '@/components/Chat/ChatToolbar'
import type { Variable } from '@/views/Workflow/components/Properties/VariableList/types'
import type { Variable as AppVariable } from '@/views/ApplicationConfig/components/VariableList/types'
import type { FeaturesConfigForm } from '@/views/ApplicationConfig/types';
import { getFileStatusById } from '@/api/fileStorage';
import { replaceVariables } from '@/views/ApplicationConfig/Agent'
const Conversation: FC = () => {
const { t } = useTranslation()
@@ -84,11 +86,11 @@ const Conversation: FC = () => {
if (shareToken && token) {
getExperienceConfig(token)
.then(res => {
const response = res as { variables: Variable[]; features: FeaturesConfigForm; app_type: string; memory?: boolean; }
const response = res as { variables: Variable[]; features: FeaturesConfigForm; app_type: string; memory: boolean; }
toolbarRef.current?.setVariables(response.variables || [])
setConfig(response)
setFeatures(response.features)
setIsHasMemory((response.app_type === 'workflow' && response.memory) || (response.app_type !== 'workflow'))
setIsHasMemory((response.app_type === 'workflow' && response.memory) || response.memory)
})
} else {
setChatList([])
@@ -375,6 +377,17 @@ const Conversation: FC = () => {
})
}
const handleChangeVariables = (variables: Variable[]) => {
setChatList(prev => {
const firstMsg = prev[0]
console.log('firstMsg', firstMsg)
if (firstMsg && firstMsg.role === 'assistant' && firstMsg.content && features?.opening_statement.enabled && features?.opening_statement.statement && variables.length > 0) {
firstMsg.content = replaceVariables(features?.opening_statement.statement, variables as unknown as AppVariable[])
}
return [firstMsg, ...prev.slice(1)]
})
}
console.log('chatList', chatList)
return (
@@ -460,7 +473,8 @@ const Conversation: FC = () => {
}
}}
rightExtra={
<Flex align="center" justify="end" gap={8}>
(features?.web_search?.enabled || isHasMemory)
? <Flex align="center" justify="end" gap={8}>
{features?.web_search?.enabled &&
<Tooltip title={t('memoryConversation.web_search')}>
<Flex justify="center" align="center"
@@ -496,7 +510,9 @@ const Conversation: FC = () => {
</Tooltip>
}
</Flex>
: undefined
}
onVariablesChange={handleChangeVariables}
/>
</Chat>
</div>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:56:54
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 18:28:18
* @Last Modified time: 2026-03-26 15:43:29
*/
/**
* Emotion Engine Configuration Page
@@ -118,8 +118,8 @@ const EmotionEngine: React.FC = () => {
}
return (
<Row gutter={[16, 16]}>
<Col span={12}>
<Row gutter={[16, 16]} className="rb:h-full!">
<Col span={12} className="rb:h-full!">
<RbCard
title={t('emotionEngine.emotionEngineConfig')}
headerType="borderless"
@@ -128,7 +128,7 @@ const EmotionEngine: React.FC = () => {
<Button block onClick={handleReset}>{t('common.reset')}</Button>
<Button type="primary" loading={loading} block onClick={handleSave}>{t('common.save')}</Button>
</Space>}
className="rb:h-[calc(100vh-76px)]!"
className="rb:h-full!"
bodyClassName="rb:h-[calc(100%-54px)] rb:overflow-y-auto! rb:p-3! rb:pt-0!"
>
<Form
@@ -207,12 +207,12 @@ const EmotionEngine: React.FC = () => {
</Form>
</RbCard>
</Col>
<Col span={12}>
<Col span={12} className="rb:h-full!">
<RbCard
title={t('emotionEngine.emotionEngineConfig')}
headerType="borderless"
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
className="rb:h-[calc(100vh-76px)]!"
className="rb:h-full!"
bodyClassName="rb:h-[calc(100%-54px)] rb:overflow-y-auto! rb:p-3! rb:pt-0!"
>
<Flex vertical gap={24} className="rb:text-[#212332]">

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 17:00:12
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 18:29:00
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-26 15:47:37
*/
/**
* Forgetting Engine Configuration Page
@@ -155,8 +155,8 @@ const ForgettingEngine: React.FC = () => {
}
return (
<Row gutter={12}>
<Col span={12}>
<Row gutter={12} className="rb:h-full!">
<Col span={12} className="rb:h-full!">
<RbCard
title={t('forgettingEngine.forgettingEngineConfigParams')}
extra={<Space>
@@ -165,7 +165,7 @@ const ForgettingEngine: React.FC = () => {
</Space>}
headerType="borderless"
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
className="rb:h-[calc(100vh-76px)]!"
className="rb:h-full!"
bodyClassName="rb:h-[calc(100%-54px)] rb:overflow-y-auto! rb:p-3! rb:pt-0!"
>
<Form
@@ -226,7 +226,7 @@ const ForgettingEngine: React.FC = () => {
</Form>
</RbCard>
</Col>
<Col span={12}>
<Col span={12} className="rb:h-full!">
<RbCard
title={t('forgettingEngine.forgettingCurve')}
headerType="borderless"

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:16:45
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-10 11:57:35
* @Last Modified time: 2026-03-26 14:30:27
*/
/**
* Pie Chart Card Component
@@ -33,7 +33,7 @@ const PieCard: FC<PieCardProps> = ({ chartData, loading }) => {
>
{loading
? <Loading size={249} />
: <PieChart chartData={chartData} />
: <PieChart chartData={chartData} itemGap={24} />
}
</Card>
)

View File

@@ -75,7 +75,7 @@ const GuideCard: React.FC = () => {
<div className="rb:mt-2 rb:pl-3 rb:pr-4">
<Button ref={startButtonRef} block className='rb:gap-1 rb:flex rb:items-center' onClick={handleStartGuide}>
<span className='rb:text-xs'>{t('index.viewGuide')}</span>
<div className="rb:size-4 rb:bg-cover rb:bg-[url('src/assets/images/common/arrow_right_dark.svg')]"></div>
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/arrow_right_dark.svg')]"></div>
</Button>
</div>
</div>

View File

@@ -7,7 +7,7 @@
* @LastEditTime: 2025-12-19 20:19:59
*/
import { useEffect, useState, useRef, type FC } from 'react';
import { useNavigate, useParams, useLocation } from 'react-router-dom';
import { useNavigate, useParams, useLocation, useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useBreadcrumbManager, type BreadcrumbPath } from '@/hooks/useBreadcrumbManager';
import { Button, Spin, message, Switch } from 'antd';
@@ -29,11 +29,16 @@ const DocumentDetails: FC = () => {
const { updateBreadcrumbs } = useBreadcrumbManager({
breadcrumbType: 'detail'
});
const [searchParams] = useSearchParams();
const {
documentId,
parentId: locationParentId,
breadcrumbPath
} = (location.state || {}) as {
} = ({
documentId: searchParams.get('documentId') ?? undefined,
parentId: searchParams.get('parentId') ?? undefined,
...(location.state || {})
}) as {
documentId?: string;
parentId?: string;
breadcrumbPath?: BreadcrumbPath;

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:42:12
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 16:25:23
* @Last Modified time: 2026-03-26 15:48:43
*/
/**
* Member Management Page
@@ -105,7 +105,7 @@ const MemberManagement: React.FC = () => {
];
return (
<div className="rb:h-[calc(100vh-80px)] rb:overflow-hidden rb:bg-white rb:rounded-lg rb:pt-3 rb:px-3">
<div className="rb:h-full rb:overflow-hidden rb:bg-white rb:rounded-lg rb:pt-3 rb:px-3">
<Flex justify="end" className="rb:px-1! rb:mb-3!">
<Button type="primary" onClick={() => handleEdit()}>
{t('member.createMember')}
@@ -117,6 +117,7 @@ const MemberManagement: React.FC = () => {
columns={columns}
rowKey="id"
pagination={false}
scrollY="calc(100vh - 248px)"
/>
<MemberModal

View File

@@ -1,40 +0,0 @@
/**
* Card Component
* Styled wrapper for conversation and analysis panels
* Provides consistent layout and styling
*/
import { Card } from 'antd'
import { type FC, type ReactNode } from 'react'
/**
* Component props
*/
interface RbCardProps {
children: ReactNode;
title: string;
bodyClassName?: string;
style?: React.CSSProperties;
}
const RbCard: FC<RbCardProps> = ({ children, title, bodyClassName, style, ...props }) => {
return (
<Card
title={title}
classNames={{
header: "rb:min-h-[40px]! rb:p-[0_16px]! rb:rounded-[12px_12px_0_0]! rb:text-[14px]! rb:leading-[20px]! rb:font-medium! rb:border-b-[#DFE4ED]",
body: `rb:h-[calc(100%-40px)] rb:p-[16px]! ${bodyClassName || ''}`,
}}
style={{
borderRadius: '12px',
borderColor: '#DFE4ED',
background: '#FBFDFF',
height: 'calc(100vh - 152px)',
...style
}}
{...props}
>
{children}
</Card>
)
}
export default RbCard

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:09:03
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-20 10:22:08
* @Last Modified time: 2026-03-26 15:00:15
*/
/**
* Memory Conversation Page
@@ -174,14 +174,14 @@ const MemoryConversation: FC = () => {
/>
</Col>
</Row>
<Row gutter={16}>
<Row gutter={16} className="rb:h-[calc(100%-48px)]!">
<Col span={12}>
<RbCard
title={t('memoryConversation.conversationContent')}
headerType="borderless"
headerClassName="rb:min-h-[52px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:px-3! rb:py-0! rb:h-[calc(100%-52px)]!"
className="rb:h-[calc(100vh-124px)]!"
className="rb:h-full!"
>
<Chat
empty={
@@ -213,7 +213,7 @@ const MemoryConversation: FC = () => {
headerType="borderless"
headerClassName="rb:min-h-[52px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-52px)]! rb:overflow-y-auto!"
className="rb:h-[calc(100vh-124px)]!"
className="rb:h-full!"
>
{loading ?
<Skeleton active />

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:30:11
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 11:40:38
* @Last Modified time: 2026-03-26 15:46:30
*/
/**
* Result Component
@@ -267,7 +267,7 @@ const Result: FC<ResultProps> = ({ loading, handleSave }) => {
title={t('memoryExtractionEngine.exampleMemoryExtractionResults')}
subTitle={t('memoryExtractionEngine.exampleMemoryExtractionResultsSubTitle')}
headerClassName="rb:pb-0! rb:pt-4!"
bodyClassName="rb:h-[calc(100vh-163px)]! rb:overflow-y-auto rb:p-[16px_20px]!"
bodyClassName="rb:h-[calc(100%-50px)]! rb:overflow-y-auto rb:p-[16px_20px]!"
extra={<Space size={8}>
<Button
icon={<div className="rb:size-3.5 rb:bg-cover rb:bg-[url('@/assets/images/common/save.svg')]"></div>}
@@ -281,6 +281,7 @@ const Result: FC<ResultProps> = ({ loading, handleSave }) => {
onClick={handleRun}
>{t('memoryExtractionEngine.debug')}</Button>
</Space>}
className="rb:h-full!"
>
{/* <RbAlert color="orange" icon={<ExclamationCircleFilled />} className="rb:mb-3!">
{t('memoryExtractionEngine.warning')}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:30:02
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-24 14:07:35
* @Last Modified time: 2026-03-26 15:45:42
*/
/**
* Memory Extraction Engine Configuration Page
@@ -123,10 +123,10 @@ const MemoryExtractionEngine: FC = () => {
</Tooltip>
</Flex>
<Row gutter={12}>
<Col span={12}>
<Form form={form}>
<Flex vertical gap={12} className="rb:h-[calc(100vh-114px)]! rb:overflow-y-auto">
<Row gutter={12} className="rb:h-[calc(100%-38px)]!">
<Col span={12} className="rb:h-full!">
<Form form={form} className="rb:h-full!">
<Flex vertical gap={12} className="rb:h-full! rb:overflow-y-auto">
<div className="rb:bg-white rb:rounded-xl rb:py-2.5 rb:px-4">
<Flex
align="center"
@@ -259,7 +259,7 @@ const MemoryExtractionEngine: FC = () => {
</Flex>
</Form>
</Col>
<Col span={12}>
<Col span={12} className="rb:h-full!">
<Result
loading={loading}
handleSave={handleSave}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:33:15
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-12 15:44:32
* @Last Modified time: 2026-03-26 14:56:00
*/
/**
* Memory Management Page
@@ -100,7 +100,10 @@ const MemoryManagement: React.FC = () => {
</div>
<BodyWrapper loading={loading} empty={data.length === 0}>
<Row gutter={[12, 12]}>
<Row
gutter={[12, 12]}
className="rb:max-h-[calc(100%-48px)] rb:overflow-y-auto"
>
{data.map((item) => (
<Col key={item.config_id} span={12}>
<RbCard

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:50:05
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 12:28:07
* @Last Modified time: 2026-03-26 15:51:08
*/
/**
* Model Management Main Page
@@ -84,8 +84,8 @@ const tabKeys = ['group', 'list', 'square']
}
return (
<Flex vertical gap={16}>
<Flex justify="space-between" align="center">
<>
<Flex justify="space-between" align="center" className="rb:mb-3!">
<PageTabs
value={activeTab}
options={formatTabItems()}
@@ -132,7 +132,7 @@ const tabKeys = ['group', 'list', 'square']
</Form>
</Flex>
<div className="rb:w-full rb:h-[calc(100vh-125px)] rb:overflow-y-auto">
<div className="rb:w-full rb:h-[calc(100%-44px)] rb:overflow-y-auto">
{activeTab === 'group' && <GroupModel ref={groupRef} query={query} handleEdit={handleEdit} />}
{activeTab === 'list' && <ModelList ref={modelListRef} query={query} handleEdit={handleEdit} handleCloseModel={() => customModelModalRef.current?.handleClose() } />}
{activeTab === 'square' && <ModelSquare query={query} />}
@@ -145,7 +145,7 @@ const tabKeys = ['group', 'list', 'square']
ref={customModelModalRef}
refresh={handleRefresh}
/>
</Flex>
</>
)
}

View File

@@ -2,17 +2,17 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 16:50:22
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 14:03:13
* @Last Modified time: 2026-03-25 18:35:13
*/
/**
* Utility functions for Model Management
*/
import bedrockIcon from '@/assets/images/model/bedrock.svg'
import bedrockIcon from '@/assets/images/model/bedrock.png'
import dashscopeIcon from '@/assets/images/model/dashscope.png'
import gpustackIcon from '@/assets/images/model/gpustack.png'
import ollamaIcon from '@/assets/images/model/ollama.svg'
import openaiIcon from '@/assets/images/model/openai.svg'
import ollamaIcon from '@/assets/images/model/ollama.png'
import openaiIcon from '@/assets/images/model/openai.png'
import xinferenceIcon from '@/assets/images/model/xinference.svg'
import volcanoIcon from '@/assets/images/model/volcano.png'

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 14:10:20
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 12:16:23
* @Last Modified time: 2026-03-26 18:55:37
*/
import { type FC, useEffect, useState, useRef } from 'react'
import { useParams, useNavigate } from 'react-router-dom';
@@ -100,58 +100,60 @@ const Detail: FC = () => {
return (
<>
<PageHeader
title={<Space>
{data.scene_name}
{data.is_system_default ? <Tag color="warning">{t('common.default')}</Tag> : undefined}
<Tooltip title={data.scene_description}>
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/question.svg')]"></div>
</Tooltip>
</Space>}
extra={<Space size={12}>
{data.is_system_default ? undefined : (<Space>
<Button type="primary" ghost className="rb:h-6! rb:px-2! rb:leading-5.5!" onClick={handleAdd}>+ {t('ontology.addClass')}</Button>
<Button className="rb:h-6! rb:px-2! rb:leading-5.5!" type="primary" onClick={handleExtract}>+ {t('ontology.extract')}</Button>
</Space>)}
<Flex align="center" className="rb:leading-5 rb:text-[14px] rb:text-[#5B6167] rb:font-regular rb:cursor-pointer" onClick={() => navigate(-1)}>
<div
className="rb:mr-2 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/logout.svg')]"
></div>
{t('common.return')}
</Flex>
</Space>}
/>
<Flex vertical className="rb:h-screen!">
<PageHeader
title={<Space>
{data.scene_name}
{data.is_system_default ? <Tag color="warning">{t('common.default')}</Tag> : undefined}
<Tooltip title={data.scene_description}>
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/question.svg')]"></div>
</Tooltip>
</Space>}
extra={<Space size={12}>
{data.is_system_default ? undefined : (<Space>
<Button type="primary" ghost className="rb:h-6! rb:px-2! rb:leading-5.5!" onClick={handleAdd}>+ {t('ontology.addClass')}</Button>
<Button className="rb:h-6! rb:px-2! rb:leading-5.5!" type="primary" onClick={handleExtract}>+ {t('ontology.extract')}</Button>
</Space>)}
<Flex align="center" className="rb:leading-5 rb:text-[14px] rb:text-[#5B6167] rb:font-regular rb:cursor-pointer" onClick={() => navigate(-1)}>
<div
className="rb:mr-2 rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/logout.svg')]"
></div>
{t('common.return')}
</Flex>
</Space>}
/>
<div className="rb:h-[calc(100vh-64px)] rb:overflow-y-auto rb:p-3">
<Row gutter={12} className="rb:mb-4">
<Col span={6} offset={18}>
<SearchInput
placeholder={t('ontology.classSearchPlaceholder')}
onSearch={(value) => setQuery({ class_name: value })}
className="rb:w-full!"
/>
</Col>
</Row>
<BodyWrapper loading={loading} empty={!data.items?.length}>
<Row gutter={[16, 16]}>
{data.items?.map(item => (
<Col key={item.class_id} span={6}>
<RbCard
title={item.class_name}
extra={data.is_system_default ? undefined : (<div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/common/delete.svg')] rb:hover:bg-[url('@/assets/images/common/delete_hover.svg')]"
onClick={() => handleDelete(item)}
></div>)}
>
<Tooltip title={item.class_description}>
<div className="rb:h-10 rb:text-[#5B6167] rb:leading-5 rb:font-regular rb:wrap-break-word rb:line-clamp-2">{item.class_description}</div>
</Tooltip>
</RbCard>
</Col>
))}
<div className="rb:flex-1 rb:p-3 rb:overflow-hidden">
<Row gutter={12} className="rb:mb-4">
<Col span={6} offset={18}>
<SearchInput
placeholder={t('ontology.classSearchPlaceholder')}
onSearch={(value) => setQuery({ class_name: value })}
className="rb:w-full!"
/>
</Col>
</Row>
</BodyWrapper>
</div>
<BodyWrapper loading={loading} empty={!data.items?.length}>
<Row gutter={[16, 16]} className="rb:max-h-[calc(100%-48px)] rb:overflow-y-auto">
{data.items?.map(item => (
<Col key={item.class_id} span={6}>
<RbCard
title={item.class_name}
extra={data.is_system_default ? undefined : (<div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/common/delete.svg')] rb:hover:bg-[url('@/assets/images/common/delete_hover.svg')]"
onClick={() => handleDelete(item)}
></div>)}
>
<Tooltip title={item.class_description}>
<div className="rb:h-10 rb:text-[#5B6167] rb:leading-5 rb:font-regular rb:wrap-break-word rb:line-clamp-2">{item.class_description}</div>
</Tooltip>
</RbCard>
</Col>
))}
</Row>
</BodyWrapper>
</div>
</Flex>
<OntologyClassModal
ref={ontologyClassModalRef}

View File

@@ -8,7 +8,7 @@ const Header:FC<{ title: string; desc: string; className?: string; }> = ({
return (
<div className={`rb:pl-2 ${className}`}>
<div className="rb:text-[#212332] rb:font-[MiSans-Bold] rb:font-bold rb:text-[16px] rb:leading-5.5">{title}</div>
<div className="rb:text-[#5B6167] rb:text-[12px] rb:leading-4 rb:mt-2 rb:pb-1">{desc}</div>
<div className="rb:text-[#5B6167] rb:text-[12px] rb:leading-4 rb:mt-2">{desc}</div>
</div>
)
}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:44:15
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-20 13:52:09
* @Last Modified time: 2026-03-26 14:31:50
*/
/**
* Prompt Editor Component
@@ -193,7 +193,7 @@ const Prompt: FC = () => {
bodyClassName="rb:px-4! rb:pt-0! rb:pb-3!"
>
<ChatContent
classNames="rb:h-[calc(100vh-265px)] rb:mb-[12px]!"
classNames="rb:h-[calc(100vh-257px)] rb:mb-[12px]!"
contentClassNames="rb:max-w-75!"
empty={<Empty url={ConversationEmptyIcon} title={t(`prompt.promptChatEmpty`)} isNeedSubTitle={false} size={[140, 100]} className="rb:h-full" />}
data={chatList || []}
@@ -275,10 +275,10 @@ const Prompt: FC = () => {
{values?.current_prompt
? <Editor
ref={editorRef}
className="rb:h-[calc(100vh-201px)] rb:bg-white! rb:border-none! rb:p-0! rb:text-[#212332] rb:leading-5"
className="rb:h-[calc(100vh-193px)] rb:bg-white! rb:border-none! rb:p-0! rb:text-[#212332] rb:leading-5"
onChange={(value) => form.setFieldValue('current_prompt', value)}
/>
: <Empty url={analysisEmptyIcon} title={t(`prompt.promptPlaceholder`)} isNeedSubTitle={false} size={[270, 170]} className="rb:h-[calc(100vh-201px)] rb:w-70 rb:mx-auto! rb:text-center! rb:text-[12px]! rb:leading-4!" />
: <Empty url={analysisEmptyIcon} title={t(`prompt.promptPlaceholder`)} isNeedSubTitle={false} size={[270, 170]} className="rb:h-[calc(100vh-193px)] rb:w-70 rb:mx-auto! rb:text-center! rb:text-[12px]! rb:leading-4!" />
}
</Form.Item>
</RbCard>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:44:04
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-09 12:18:09
* @Last Modified time: 2026-03-26 14:36:33
*/
/**
* Prompt History Component
@@ -126,6 +126,7 @@ const History: React.FC = () => {
</div>
</RbCard>
)}
heightClass="rb:h-[calc(100vh-126px)]!"
/>
<PromptDetail

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:46:47
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 11:44:16
* @Last Modified time: 2026-03-26 18:57:08
*/
/**
* Self Reflection Engine Configuration Page
@@ -169,8 +169,8 @@ const SelfReflectionEngine: React.FC = () => {
}
return (
<Row gutter={[16, 16]}>
<Col span={12}>
<Row gutter={[16, 16]} className="rb:h-full!">
<Col span={12} className="rb:h-full!">
<RbCard
title={t('reflectionEngine.reflectionEngineConfig')}
extra={<Space>
@@ -179,7 +179,7 @@ const SelfReflectionEngine: React.FC = () => {
</Space>}
headerType="borderless"
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
className="rb:h-[calc(100vh-76px)]!"
className="rb:h-full!"
bodyClassName="rb:h-[calc(100%-54px)] rb:overflow-y-auto! rb:p-4! rb:pt-0!"
>
<Form
@@ -252,8 +252,8 @@ const SelfReflectionEngine: React.FC = () => {
</Form>
</RbCard>
</Col>
<Col span={12}>
<Space size={16} direction="vertical" className="rb:w-full">
<Col span={12} className="rb:h-full!">
<Flex gap={16} vertical className="rb:h-full!">
<RbCard
title={t('memoryExtractionEngine.example')}
>
@@ -346,7 +346,7 @@ const SelfReflectionEngine: React.FC = () => {
</RbCard>
)}
</>}
</Space>
</Flex>
</Col>
</Row>
);

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:48:59
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 15:33:38
* @Last Modified time: 2026-03-26 14:43:20
*/
/**
* Space Management Page
@@ -12,7 +12,7 @@
import React, { useEffect, useState, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { List, Button, Flex, Space as AntSpace, Tooltip } from 'antd';
import { Button, Flex, Space as AntSpace, Tooltip, Row, Col } from 'antd';
import type { Space, SpaceModalRef } from './types';
import SpaceModal from './components/SpaceModal';
@@ -68,11 +68,12 @@ const SpaceManagement: React.FC = () => {
{t('space.createSpace')}
</Button>
<BodyWrapper loading={loading} empty={data.length === 0}>
<List
grid={{ gutter: 16, column: 4 }}
dataSource={data}
renderItem={(item) => (
<List.Item key={item.id}>
<Row
gutter={[16, 16]}
className="rb:max-h-[calc(100%-48px)] rb:overflow-y-auto"
>
{data.map(item => (
<Col key={item.id} span={6}>
<RbCard
avatarUrl={item.icon}
avatarText={item.name[0]}
@@ -91,10 +92,9 @@ const SpaceManagement: React.FC = () => {
</Button>}
>
</RbCard>
</List.Item>
)}
className="rb:h-[calc(100vh-124px)] rb:overflow-y-auto rb:overflow-x-hidden"
/>
</Col>
))}
</Row>
</BodyWrapper>
<SpaceModal

View File

@@ -3,7 +3,6 @@ import {
Row,
Col,
App,
List,
Space,
Flex,
Tooltip,
@@ -69,11 +68,12 @@ const Custom = forwardRef<CustomRef, { getStatusTag: (status: string) => ReactNo
return (
<>
<BodyWrapper loading={loading} empty={data.length === 0}>
<List
grid={{ gutter: 16, column: 3 }}
dataSource={data}
renderItem={(item) => (
<List.Item key={item.id}>
<Row
gutter={[16, 16]}
className="rb:max-h-[calc(100%-48px)] rb:overflow-y-auto"
>
{data.map((item) => (
<Col span={8} key={item.id}>
<RbCard
title={
<Flex justify="space-between" gap={16}>
@@ -109,7 +109,6 @@ const Custom = forwardRef<CustomRef, { getStatusTag: (status: string) => ReactNo
}
isNeedTooltip={false}
>
{item.tags?.length > 0
? <Flex gap={8} wrap align="center">
<Flex gap={6}>
@@ -142,10 +141,9 @@ const Custom = forwardRef<CustomRef, { getStatusTag: (status: string) => ReactNo
</Col>
</Row>
</RbCard>
</List.Item>
)}
className="rb:h-[calc(100vh-178px)] rb:overflow-y-auto rb:overflow-x-hidden"
/>
</Col>
))}
</Row>
</BodyWrapper>
{/* 添加服务弹窗组件 */}

View File

@@ -1,6 +1,5 @@
import React, { useState, useRef, useEffect, type ReactNode } from 'react';
import {
List,
Flex,
Space,
Tooltip,
@@ -68,13 +67,14 @@ const Inner: React.FC<{ getStatusTag: (status: string) => ReactNode; keyword?: s
}
return (
<div>
<>
<BodyWrapper loading={loading} empty={data.length === 0}>
<List
grid={{ gutter: 16, column: 3 }}
dataSource={data}
renderItem={(item) => (
<List.Item key={item.id} className='rb:h-full!'>
<Row
gutter={[12, 12]}
className="rb:max-h-[calc(100%-48px)] rb:overflow-y-auto"
>
{data.map((item) => (
<Col span={8} key={item.id}>
<RbCard
title={
<Flex justify="space-between" gap={16}>
@@ -130,21 +130,20 @@ const Inner: React.FC<{ getStatusTag: (status: string) => ReactNode; keyword?: s
</Col>
</>
: item.config_data.tool_class === 'JsonTool'
? <Col span={24}>
? <Col span={24}>
<div className="rb:text-[#5B6167] rb:mb-1">{t('tool.jsonEg')}</div>
{InnerConfigData[item.config_data.tool_class].eg}
</Col>
: <Col span={24}>
<div className="rb:text-[#5B6167] rb:mb-1">{t('configStatus')}</div>
{t(`tool.${item.status}_desc`)}
</Col>
: <Col span={24}>
<div className="rb:text-[#5B6167] rb:mb-1">{t('configStatus')}</div>
{t(`tool.${item.status}_desc`)}
</Col>
}
</Row>
</RbCard>
</List.Item>
)}
className="rb:h-[calc(100vh-178px)] rb:overflow-y-auto rb:overflow-x-hidden"
/>
</Col>
))}
</Row>
</BodyWrapper>
<TimeToolModal
@@ -157,7 +156,7 @@ const Inner: React.FC<{ getStatusTag: (status: string) => ReactNode; keyword?: s
ref={innerToolModalRef}
refreshTable={getData}
/>
</div>
</>
);
};

View File

@@ -434,7 +434,7 @@ const Market: React.FC<{ getStatusTag?: (status: string) => ReactNode }> = () =>
footer={<Flex justify="space-between" align="center" className="rb:text-[#5B6167] rb:text-[12px] rb:mb-1!">
{mcp.publisher && <span>{mcp.publisher.startsWith('@') ? mcp.publisher : `@${mcp.publisher}`}</span>}
{mcp.view_count && <Space size={4}>
<div className="rb:size-4 rb:bg-cover rb:bg-[url('src/assets/images/common/global_outline.svg')]"></div>
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/global_outline.svg')]"></div>
{mcp.view_count.toLocaleString()}
</Space>}
</Flex>}

View File

@@ -1,11 +1,11 @@
import { useState, useRef, useEffect, forwardRef, useImperativeHandle, type ReactNode } from 'react';
import {
App,
List,
Space,
Tooltip,
Dropdown,
Flex,
Row, Col,
} from 'antd';
import { useTranslation } from 'react-i18next';
@@ -84,11 +84,12 @@ const Mcp = forwardRef<McpRef, { getStatusTag: (status: string) => ReactNode; ke
return (
<>
<BodyWrapper loading={loading} empty={data?.length === 0}>
<List
grid={{ gutter: 16, column: 3 }}
dataSource={data}
renderItem={(item) => (
<List.Item key={item.id}>
<Row
gutter={[16, 16]}
className="rb:max-h-[calc(100%-48px)] rb:overflow-y-auto"
>
{data.map((item) => (
<Col span={8} key={item.id}>
<RbCard
title={
<Flex justify="space-between" gap={16}>
@@ -137,12 +138,12 @@ const Mcp = forwardRef<McpRef, { getStatusTag: (status: string) => ReactNode; ke
</div>
</Flex>
<div className="rb:text-[#5B6167] rb:leading-4.5 rb:text-[12px] rb:mt-4">{t('tool.last_health_check')}: {formatDateTime(item.config_data?.last_health_check)}</div>
</RbCard>
</List.Item>
)}
className="rb:h-[calc(100vh-124px)] rb:overflow-y-auto rb:overflow-x-hidden"
/>
</Col>
))}
<Col span={8}></Col>
</Row>
</BodyWrapper>
{/* 添加服务弹窗组件 */}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:51:08
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 16:45:18
* @Last Modified time: 2026-03-26 14:53:41
*/
/**
* User Management Page
@@ -142,7 +142,7 @@ const UserManagement: React.FC = () => {
];
return (
<div className="rb:h-[calc(100vh-80px)] rb:overflow-hidden rb:bg-white rb:rounded-lg rb:pt-3 rb:px-3">
<div className="rb:h-full rb:overflow-hidden rb:bg-white rb:rounded-lg rb:pt-3 rb:px-3">
<Flex justify="space-between" align="center" className="rb:px-1! rb:mb-3!">
<div className="rb:gont-[MiSans-Bold] rb:font-bold rb:text-[#212332] rb:leading-5">{t('user.userList')}</div>
<Button type="primary" onClick={handleCreate}>
@@ -159,7 +159,7 @@ const UserManagement: React.FC = () => {
columns={columns}
rowKey="id"
isScroll={true}
scrollY="calc(100vh - 256px)"
scrollY="calc(100vh - 248px)"
/>
<CreateModal

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:53:44
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 15:23:18
* @Last Modified time: 2026-03-26 14:58:48
*/
/**
* User Memory Page
@@ -12,15 +12,15 @@
import { useEffect, useState, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom'
import { Row, Col, Skeleton, Form, Flex, Tooltip } from 'antd';
import { Row, Col, Form, Flex, Tooltip } from 'antd';
import Empty from '@/components/Empty'
import type { Data } from './types'
import { getUserMemoryList } from '@/api/memory';
import { useUser } from '@/store/user'
import RbCard from '@/components/RbCard/Card'
import SearchInput from '@/components/SearchInput';
import RbStatistic from '@/components/RbStatistic';
import BodyWrapper from '@/components/Empty/BodyWrapper'
export default function UserMemory() {
const { t } = useTranslation();
@@ -91,52 +91,52 @@ export default function UserMemory() {
</Col>
</Row>
</Form>
{loading ?
<Skeleton active />
: filterData.length > 0 ? (
<Row gutter={[12, 12]}>
{filterData.map((item, index) => {
const { end_user, memory_num, memory_config } = item as Data;
const name = end_user?.other_name && end_user?.other_name !== '' ? end_user?.other_name : end_user?.id
return (
<Col key={index} span={8}>
<RbCard
title={<Flex gap={4}>
<div className="rb:size-6 rb:text-center rb:font-semibold rb:leading-6 rb:rounded-md rb:text-white rb:bg-[#155EEF]">{name[0]}</div>
<Tooltip title={name || '-'}><div className={`rb:flex-1 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap`}>{name || '-'}</div></Tooltip>
</Flex>}
headerType="border"
headerClassName="rb:h-[48px]! rb:mx-4!"
bodyClassName="rb:py-3! rb:px-4!"
className="rb:cursor-pointer"
onClick={() => handleViewDetail(end_user.id)}
>
<Row>
<Col span={12}>
<RbStatistic title={t('userMemory.capacity')} value={memory_num?.total || 0} suffix={t('userMemory.memoryNum')} />
</Col>
<Col span={12}>
<RbStatistic title={t('userMemory.type')} value={t(`userMemory.${item.type || 'person'}`)} />
</Col>
</Row>
<BodyWrapper loading={loading} empty={data.length === 0}>
<Row
gutter={[12, 12]}
className="rb:max-h-[calc(100%-48px)] rb:overflow-y-auto"
>
{filterData.map((item, index) => {
const { end_user, memory_num, memory_config } = item as Data;
const name = end_user?.other_name && end_user?.other_name !== '' ? end_user?.other_name : end_user?.id
return (
<Col key={index} span={8}>
<RbCard
title={<Flex gap={4}>
<div className="rb:size-6 rb:text-center rb:font-semibold rb:leading-6 rb:rounded-md rb:text-white rb:bg-[#155EEF]">{name[0]}</div>
<div className="rb:relative rb:z-2 rb:mt-3 rb:bg-[#F6F6F6] rb:rounded-lg rb:py-2 rb:px-3 rb:leading-5" onClick={handleViewMemoryConfig}>
<Flex align="center" justify="space-between" className="rb:text-[#5B6167]">
{t('userMemory.memory_config_name')}
<div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/userMemory/arrow_right_dark.svg')]"
></div>
</Flex>
<div className="rb:font-medium rb:text-[#212332] rb:mt-1">{memory_config?.memory_config_name || '-'}</div>
</div>
</RbCard>
</Col>
)
})}
</Row>
) : <Empty />
}
<Tooltip title={name || '-'}><div className={`rb:flex-1 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap`}>{name || '-'}</div></Tooltip>
</Flex>}
headerType="border"
headerClassName="rb:h-[48px]! rb:mx-4!"
bodyClassName="rb:py-3! rb:px-4!"
className="rb:cursor-pointer"
onClick={() => handleViewDetail(end_user.id)}
>
<Row>
<Col span={12}>
<RbStatistic title={t('userMemory.capacity')} value={memory_num?.total || 0} suffix={t('userMemory.memoryNum')} />
</Col>
<Col span={12}>
<RbStatistic title={t('userMemory.type')} value={t(`userMemory.${item.type || 'person'}`)} />
</Col>
</Row>
<div className="rb:relative rb:z-2 rb:mt-3 rb:bg-[#F6F6F6] rb:rounded-lg rb:py-2 rb:px-3 rb:leading-5" onClick={handleViewMemoryConfig}>
<Flex align="center" justify="space-between" className="rb:text-[#5B6167]">
{t('userMemory.memory_config_name')}
<div
className="rb:size-5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/userMemory/arrow_right_dark.svg')]"
></div>
</Flex>
<div className="rb:font-medium rb:text-[#212332] rb:mt-1">{memory_config?.memory_config_name || '-'}</div>
</div>
</RbCard>
</Col>
)
})}
</Row>
</BodyWrapper>
</div>
);
}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:57:26
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-09 14:28:34
* @Last Modified time: 2026-03-26 18:59:53
*/
/**
* Neo4j User Memory Detail View
@@ -75,7 +75,7 @@ const Neo4j: FC = () => {
return (
<div className="rb:h-screen rb:w-screen rb:p-3 rb:relative" onClick={() => setSelectedKey(null)}>
<Flex className="rb:h-[calc(100vh-24px)]" gap={12}>
<Flex className="rb:h-full!" gap={12}>
<Flex gap={15} vertical justify="space-between" align="center" className="rb:h-full! rb:px-4! rb:pt-6! rb:pb-5! rb:bg-white rb:w-20 rb:rounded-xl">
<Flex gap={15} vertical>
<Popover

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 17:57:11
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-20 11:27:53
* @Last Modified time: 2026-03-27 10:26:31
*/
/**
* RAG User Memory Detail View
@@ -113,10 +113,11 @@ const Rag: FC = () => {
})
}
return (
<Row gutter={[16, 16]}>
<Row gutter={[16, 16]} className="rb:h-full!">
<Col span={8}>
<RbCard
bodyClassName="rb:p-3! rb:pt-4! rb:h-[calc(100vh-76px)]"
bodyClassName="rb:p-3! rb:pt-4!"
className="rb:h-full!"
>
<Flex align="center" gap={12} className="rb:mb-6!">
<div className="rb:size-12 rb:text-center rb:font-semibold rb:text-[28px] rb:leading-12 rb:rounded-xl rb:text-white rb:bg-[#155EEF]">{name?.[0]}</div>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:34:23
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-11 15:03:05
* @Last Modified time: 2026-03-27 11:09:52
*/
/**
* About Me Component
@@ -67,12 +67,12 @@ const AboutMe = forwardRef<AboutMeRef, { className?: string; }>(({ className },
title={t('userMemory.aboutMe')}
headerClassName="rb:min-h-[46px]!! rb:font-medium!"
className={clsx("rb:bg-[#FFFFFF]! rb:shadow-[0px_2px_6px_0px_rgba(33,35,50,0.13)]! rb:absolute! rb:w-100 rb:top-29 rb:left-26", className)}
bodyClassName="rb:px-5! rb:pb-5! rb:pt-3.75! rb:max-h-[calc(100vh-176px)] rb:overflow-y-auto!"
bodyClassName="rb:px-5! rb:pb-5! rb:pt-3.75! rb:max-h-[calc(100vh-186px)]! rb:overflow-y-auto!"
>
{loading
? <Skeleton className="rb:mt-4" />
: Object.keys(data).filter(key => data[key] !== null).length > 0
? <>
? <div className="rb:overflow-y-auto rb:h-full">
{data.user_summary &&
<div className="rb:font-regular rb:leading-5 rb:text-[#5B6167]">
{data.user_summary}
@@ -95,7 +95,7 @@ const AboutMe = forwardRef<AboutMeRef, { className?: string; }>(({ className },
{data.one_sentence &&
<RbAlert className="rb:mt-4! rb:text-[14px]!">{data.one_sentence}</RbAlert>
}
</>
</div>
: <Empty size={88} className="rb:mt-12 rb:mb-20.25" />
}
</RbCard>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:34:16
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 11:36:02
* @Last Modified time: 2026-03-27 11:22:10
*/
import { type FC } from 'react'
import { useTranslation } from 'react-i18next'
@@ -32,7 +32,7 @@ const ActivationMetricsPieCard: FC<ActivationMetricsPieCardProps> = ({ chartData
className="rb:h-full!"
>
{loading
? <Loading size={249} />
? <Loading size={150} />
: <PieChart
chartData={chartData as { name: string; value: number }[]}
height={214}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:34:04
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-20 11:04:52
* @Last Modified time: 2026-03-27 10:28:53
*/
import { type FC } from 'react'
import { useTranslation } from 'react-i18next'
@@ -20,8 +20,10 @@ const ConversationMemory: FC = () => {
return (
<RbCard
title={t('userMemory.conversationMemory')}
headerClassName="rb:text-[18px]! rb:leading-[24px]"
bodyClassName="rb:h-[100%]! rb:overflow-hidden rb:py-0!"
headerType="borderless"
headerClassName="rb:min-h-[54px]! rb:pt-0! rb:mb-0! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-4! rb:pt-0! rb:h-[calc(100%-54px)]!"
className="rb:h-full!"
>
<PageScrollList<string>
url={getRagContentUrl}

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:33:30
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-24 17:55:02
* @Last Modified time: 2026-03-27 11:11:09
*/
/**
* End User Profile Component
@@ -85,7 +85,7 @@ const EndUserProfile = forwardRef<EndUserProfileRef, EndUserProfileProps>(({ cla
}
headerClassName="rb:min-h-[46px]!! rb:font-medium!"
className={clsx("rb:bg-[#FFFFFF]! rb:shadow-[0px_2px_6px_0px_rgba(33,35,50,0.13)]! rb:absolute! rb:w-80 rb:top-29 rb:left-26", className)}
bodyClassName="rb:px-5! rb:pb-5! rb:pt-3.75! rb:max-h-[calc(100vh-176px)] rb:overflow-auto"
bodyClassName="rb:px-5! rb:pb-5! rb:pt-3.75! rb:max-h-[calc(100vh-186px)] rb:overflow-auto"
>
{loading
? <Skeleton />

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:33:06
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 14:05:10
* @Last Modified time: 2026-03-27 11:18:47
*/
import { useEffect, useState, forwardRef, useImperativeHandle } from 'react'
import { useTranslation } from 'react-i18next'
@@ -83,7 +83,7 @@ const Habits = forwardRef<{ handleRefresh: () => void; }>((_props, ref) => {
headerType="borderless"
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-54px)] rb:overflow-y-auto!"
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
>
{loading
? <Skeleton active />

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:32:47
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-05 18:29:29
* @Last Modified time: 2026-03-27 11:11:35
*/
/**
* Interest Distribution Component
@@ -77,7 +77,7 @@ const InterestDistribution: FC<{ className?: string; }> = ({ className }) => {
title={t('userMemory.interestDistribution')}
headerClassName="rb:min-h-[46px]!! rb:font-medium!"
className={clsx("rb:bg-[#FFFFFF]! rb:shadow-[0px_2px_6px_0px_rgba(33,35,50,0.13)]! rb:absolute! rb:w-100 rb:top-29 rb:left-26", className)}
bodyClassName="rb:px-5! rb:pb-5! rb:pt-3.75! rb:max-h-[calc(100vh-176px)] rb:overflow-auto"
bodyClassName="rb:px-5! rb:pb-5! rb:pt-3.75! rb:max-h-[calc(100vh-186px)] rb:overflow-auto"
>
{loading
? <Loading size={249} />

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:32:41
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-02-05 18:35:01
* @Last Modified time: 2026-03-27 11:11:46
*/
/**
* Memory Insight Component
@@ -66,7 +66,7 @@ const MemoryInsight = forwardRef<MemoryInsightRef, { className?: string; }>(({ c
title={t('userMemory.memoryInsight')}
headerClassName="rb:min-h-[46px]!! rb:font-medium!"
className={clsx("rb:bg-[#FFFFFF]! rb:shadow-[0px_2px_6px_0px_rgba(33,35,50,0.13)]! rb:absolute! rb:w-100 rb:top-29 rb:left-26", className)}
bodyClassName="rb:px-5! rb:pb-5! rb:pt-3.75! rb:max-h-[calc(100vh-176px)] rb:overflow-auto"
bodyClassName="rb:px-5! rb:pb-5! rb:pt-3.75! rb:max-h-[calc(100vh-186px)] rb:overflow-auto"
>
{loading
? <Skeleton />

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:32:23
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 12:09:53
* @Last Modified time: 2026-03-27 11:13:27
*/
import { type FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -119,7 +119,7 @@ const PerceptualLastInfo: FC = () => {
headerType="borderless"
headerClassName="rb:min-h-[50px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-4! rb:pt-0! rb:h-[calc(100%-50px)] rb:overflow-y-auto"
className="rb:h-[calc(100vh-88px)]! rb:w-full!"
className="rb:h-full! rb:w-full!"
>
<Flex align="center" gap={8} className="rb:mb-4!">
{Object.keys(KEYS).map(key => (

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:32:07
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 11:49:29
* @Last Modified time: 2026-03-27 11:23:11
*/
import { type FC, useRef } from 'react'
import { useTranslation } from 'react-i18next'
@@ -72,7 +72,7 @@ const RecentTrendsLineCard: FC<RecentTrendsLineCardProps> = ({ chartData, series
className="rb:h-full!"
>
{loading
? <Loading size={249} />
? <Loading size={150} />
: !chartData || chartData.length === 0
? <Empty size={120} className="rb:mt-12 rb:mb-20.25" />
: <ReactEcharts

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 18:31:50
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 11:50:16
* @Last Modified time: 2026-03-27 11:36:27
*/
import { useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react'
import { useTranslation } from 'react-i18next'

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2026-02-03 18:31:36
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 15:02:11
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-27 11:13:44
*/
import { type FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -86,7 +86,7 @@ const Timeline: FC = () => {
headerType="borderless"
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:pl-5! rb:pt-0! rb:pr-3! rb:pb-4! rb:h-[calc(100%-54px)] rb:overflow-y-auto"
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
>
{loading
? <Skeleton active />

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-01-08 19:46:02
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 15:03:50
* @Last Modified time: 2026-03-27 11:20:40
*/
import { type FC, useEffect, useState } from 'react'
import clsx from 'clsx'
@@ -148,15 +148,15 @@ const EpisodicDetail: FC = () => {
}
return (
<Row gutter={16}>
<Col flex="400px">
<Row gutter={16} className="rb:h-full!">
<Col flex="400px" className="rb:h-full!">
<RbCard
title={<div className="rb:leading-5.5!">
<span className="rb:font-[MiSans-Bold] rb:font-bold">{t('episodicDetail.curResult')}</span>
<span className="rb:text-[#5B6167] rb:font-regular!"> ({data.total || 0}{t('episodicDetail.unix')})</span>
</div>}
headerType="borderless"
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
headerClassName="rb:min-h-[38px]! rb:pt-3! rb:mb-0!"
bodyClassName="rb:p-3! rb:pb-0! rb:h-[calc(100%-38px)]!"
>
@@ -231,11 +231,11 @@ const EpisodicDetail: FC = () => {
}
</RbCard>
</Col>
<Col flex="1">
<Col flex="1" className="rb:h-full!">
<RbCard
title={selected?.title}
headerType="borderless"
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-54px)]! rb:overflow-y-auto"
>

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2026-01-10 17:35:17
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 15:05:06
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-27 11:19:38
*/
import { type FC, useEffect, useState, useRef } from 'react'
import { useTranslation } from 'react-i18next'
@@ -132,14 +132,14 @@ const ExplicitDetail: FC = () => {
return () => { chartInstance.current?.dispose(); chartInstance.current = null }
}, [data.semantic_memories])
return (
<Row gutter={12} className="rb:h-full">
<Col span={12}>
<Row gutter={12} className="rb:h-full!">
<Col span={12} className="rb:h-full!">
<RbCard
title={t('explicitDetail.episodic_memories')}
headerType="borderless"
headerClassName="rb:min-h-[50px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-50px)] rb:overflow-y-auto!"
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
>
{loading ?
<Skeleton active />
@@ -163,13 +163,13 @@ const ExplicitDetail: FC = () => {
}
</RbCard>
</Col>
<Col span={12}>
<Col span={12} className="rb:h-full!">
<RbCard
title={t('explicitDetail.semantic_memories')}
headerType="borderless"
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-54px)] rb:overflow-y-auto!"
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
>
{loading ?
<Skeleton active />

View File

@@ -113,13 +113,13 @@ const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
}
/>
<Row gutter={12} className="rb:p-3! rb:pr-0! rb:h-[calc(100vh-64px)] rb:w-full! rb:flex-nowrap! rb:overflow-hidden!">
<Col flex="480px">
<Col flex="480px" className="rb:h-full!">
<RbCard
title={t('userMemory.relationshipEvolution')}
headerType="borderless"
headerClassName="rb:min-h-[56px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-56px)] rb:overflow-y-auto!"
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
>
<Flex vertical gap={16}>
<EmotionLine chartData={emotionData} loading={loading} />
@@ -127,13 +127,13 @@ const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
</Flex>
</RbCard>
</Col>
<Col className="rb:w-[calc(100%-480px)]!">
<Col flex="1" className="rb:h-full!">
<RbCard
title={t('userMemory.timelineMemories')}
headerType="borderless"
headerClassName="rb:min-h-[53px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0!"
className="rb:w-full!"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-53px)]!"
className="rb:w-full! rb:h-full!"
>
<BtnTabs
className="rb:mb-4!"
@@ -144,7 +144,7 @@ const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
}))}
onChange={(key: string) => setActiveTab(key)}
/>
<div className="rb:h-[calc(100vh-193px)] rb:overflow-y-auto">
<div className="rb:h-[calc(100%-42px)] rb:overflow-y-auto">
{timelineLoading
? <Skeleton active />
: !activeContent || activeContent.length === 0

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-01-08 19:46:02
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 11:56:55
* @Last Modified time: 2026-03-27 11:18:50
*/
import { forwardRef, useImperativeHandle, useRef, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -83,41 +83,39 @@ const ImplicitDetail = forwardRef<{ handleRefresh: () => void; }, { refresh: ()
}
return (
<div className="rb:h-full">
<Row gutter={12}>
<Col span={12}>
<RbCard
title={t('implicitDetail.subconscious')}
headerType="borderless"
headerClassName="rb:min-h-[50px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-54px)]"
className="rb:h-[calc(100vh-88px)]!"
>
<RadioGroupButton
value={activeTab}
options={[
{ value: 'preferences', label: t('implicitDetail.preferences') },
{ value: 'portrait', label: t('implicitDetail.portrait') },
]}
onChange={handleChangeTab}
/>
<Row gutter={12} className="rb:h-full!">
<Col span={12} className="rb:h-full!">
<RbCard
title={t('implicitDetail.subconscious')}
headerType="borderless"
headerClassName="rb:min-h-[50px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-54px)]"
className="rb:h-full!"
>
<RadioGroupButton
value={activeTab}
options={[
{ value: 'preferences', label: t('implicitDetail.preferences') },
{ value: 'portrait', label: t('implicitDetail.portrait') },
]}
onChange={handleChangeTab}
/>
<div className="rb:mt-3 rb:h-[calc(100%-32px)]">
{activeTab === 'preferences'
? <Preferences ref={preferencesRef} />
: <div className="rb:h-full rb:overflow-y-auto">
<Portrait ref={portraitRef} />
<InterestAreas ref={interestAreasRef} />
</div>
}
</div>
</RbCard>
</Col>
<Col span={12}>
<Habits ref={habitsRef} />
</Col>
</Row>
</div>
<div className="rb:mt-3 rb:h-[calc(100%-32px)]">
{activeTab === 'preferences'
? <Preferences ref={preferencesRef} />
: <div className="rb:h-full rb:overflow-y-auto">
<Portrait ref={portraitRef} />
<InterestAreas ref={interestAreasRef} />
</div>
}
</div>
</RbCard>
</Col>
<Col span={12} className="rb:h-full!">
<Habits ref={habitsRef} />
</Col>
</Row>
)
})
export default ImplicitDetail

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2026-01-08 19:46:02
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 15:09:12
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-27 11:13:19
*/
import { type FC } from 'react'
import { Row, Col } from 'antd'
@@ -22,11 +22,11 @@ import Timeline from '../components/Timeline'
const PerceptualDetail: FC = () => {
return (
<Row gutter={12}>
<Col flex="480px">
<Row gutter={12} className="rb:h-full!">
<Col flex="480px" className="rb:h-full!">
<PerceptualLastInfo />
</Col>
<Col flex="1">
<Col flex="1" className="rb:h-full!">
<Timeline />
</Col>
</Row>

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2026-01-08 19:46:02
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 15:09:49
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-27 11:17:22
*/
import { type FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -87,8 +87,8 @@ const ShortTermDetail: FC = () => {
}
return (
<Row gutter={12}>
<Col span={12}>
<Row gutter={12} className="rb:h-full!">
<Col span={12} className="rb:h-full!">
<div className="rb:grid rb:grid-cols-3 rb:gap-3 rb:mb-3">
{(['retrieval_number', 'entity', 'long_term_number'] as const).map(key => (
<Flex key={key} align="center" justify="space-between" className="rb:bg-white rb:rounded-xl rb:py-3! rb:pl-5! rb:pr-4!">
@@ -115,10 +115,10 @@ const ShortTermDetail: FC = () => {
</Space>)}
headerType="borderless"
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-54px)] rb:overflow-y-auto!"
className="rb:h-[calc(100vh-183px)]!"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-54px)]"
className="rb:h-[calc(100%-94px)]!"
>
<Flex gap={12} vertical>
<Flex gap={12} vertical className="rb:h-full! rb:overflow-y-auto!">
{loading
? <Skeleton active />
: !data.short_term || data.short_term.length === 0
@@ -189,7 +189,7 @@ const ShortTermDetail: FC = () => {
</Flex>
</RbCard>
</Col>
<Col span={12}>
<Col span={12} className="rb:h-full!">
<RbCard
title={() => (<Space size={4}>
{t('shortTermDetail.longTermTitle')}
@@ -200,7 +200,7 @@ const ShortTermDetail: FC = () => {
headerType="borderless"
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-54px)] rb:overflow-y-auto!"
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
>
<Flex vertical gap={12}>
{loading

View File

@@ -1,8 +1,8 @@
/*
* @Author: ZhaoYing
* @Date: 2025-12-19 16:54:52
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-16 15:06:29
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-27 11:35:37
*/
import { forwardRef, useImperativeHandle, useRef } from 'react'
import { Row, Col } from 'antd';
@@ -46,8 +46,8 @@ const StatementDetail = forwardRef<{ handleRefresh: () => void },{ refresh: () =
handleRefresh
}));
return (
<Row gutter={[12, 12]}>
<Col span={12}>
<Row gutter={[12, 12]} className="rb:h-full!">
<Col span={12} className="rb:h-full!">
<Row gutter={[12, 12]}>
<Col span={24}>
<WordCloud />
@@ -60,7 +60,7 @@ const StatementDetail = forwardRef<{ handleRefresh: () => void },{ refresh: () =
</Col>
</Row>
</Col>
<Col span={12}>
<Col span={12} className="rb:h-full!">
<Suggestions ref={suggestionsRef} refresh={refresh} />
</Col>
</Row>

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-01-12 14:42:02
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 11:55:36
* @Last Modified time: 2026-03-27 11:15:05
*/
import { type FC, useEffect, useState, useMemo, useRef } from 'react'
import clsx from 'clsx'
@@ -155,14 +155,14 @@ const WorkingDetail: FC = () => {
: data.length === 0
? <Empty />
:(
<Row gutter={16}>
<Col span={5}>
<Row gutter={16} className="rb:h-full!">
<Col span={5} className="rb:h-full!">
<RbCard
title={t('workingDetail.conversation')}
headerType="borderless"
headerClassName="rb:min-h-[58px]! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName='rb:p-3! rb:pt-0! rb:h-[calc(100%-58px)]'
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
>
<div id="conversation-list" className="rb:h-full! rb:overflow-y-auto">
<InfiniteScroll
@@ -197,13 +197,13 @@ const WorkingDetail: FC = () => {
</RbCard>
</Col>
{selected && <>
<Col flex="auto" className="rb:h-full">
<Col flex="auto" className="rb:h-full!">
<RbCard
title={selected.title}
headerType="borderless"
headerClassName="rb:min-h-[42px]! rb:pt-4! rb:font-[MiSans-Bold] rb:font-bold"
bodyClassName='rb:p-4! rb:pt-0! rb:h-[calc(100%-42px)]'
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
>
<div className="rb:text-[#5B6167] rb:leading-4.5 rb:text-[12px]">{timeRange}</div>
<Flex justify="space-between" align="center" className="rb:bg-[#F6F6F6] rb:rounded-lg rb:py-2.5! rb:pr-2.5! rb:pl-3.25! rb:mt-3!">
@@ -226,13 +226,13 @@ const WorkingDetail: FC = () => {
}
</RbCard>
</Col>
<Col flex='360px' className="rb:h-full">
<Col flex='360px' className="rb:h-full!">
<RbCard
title={t('workingDetail.successfulTitle')}
headerType="borderless"
headerClassName="rb:min-h-[50px]! rb:font-[MiSans-Bold] rb:font-bold rb:leading-5.5"
bodyClassName='rb:p-4! rb:pt-0! rb:h-[calc(100%-50px)] rb:overflow-y-auto!'
className="rb:h-[calc(100vh-88px)]!"
className="rb:h-full!"
>
{detailLoading
? <Skeleton active />

View File

@@ -10,7 +10,7 @@ const NodeLibrary: FC<{ collapsed: boolean; handleToggle: () => void }> = ({ col
const { t } = useTranslation()
return (
<div className={clsx("rb:overflow-hidden rb:fixed rb:left-2.5 rb:top-18.5 rb:z-1000", {
<div className={clsx("rb:h-[calc(100vh-88px)] rb:overflow-hidden rb:fixed rb:left-2.5 rb:top-18.5 rb:z-1000", {
'rb:w-65': !collapsed,
'rb:w-14': collapsed
})}>
@@ -27,9 +27,9 @@ const NodeLibrary: FC<{ collapsed: boolean; handleToggle: () => void }> = ({ col
'rb:min-h-[52px]!': collapsed
})}
className="rb:h-full! rb:hover:shadow-none!"
bodyClassName={clsx('rb:overflow-y-auto! rb:h-[calc(100vh-126px)]! rb:pt-0! rb:pb-3!', {
'rb:px-0!': collapsed,
'rb:px-3!': !collapsed
bodyClassName={clsx('rb:overflow-y-auto! rb:pt-0! rb:pb-3!', {
'rb:px-0! rb:h-[calc(100%-52px)]!': collapsed,
'rb:px-3! rb:h-[calc(100%-42px)]!': !collapsed
})}
>
<Flex vertical align={collapsed ? 'center' : undefined} gap={collapsed ? 8 : 16}>
@@ -87,8 +87,6 @@ const NodeLibrary: FC<{ collapsed: boolean; handleToggle: () => void }> = ({ col
}
</Flex>
</RbCard>
<Flex gap={12} vertical>
</Flex>
</div>
);
};

View File

@@ -2,7 +2,7 @@
* @Author: ZhaoYing
* @Date: 2026-02-03 15:39:59
* @Last Modified by: ZhaoYing
* @Last Modified time: 2026-03-25 15:08:02
* @Last Modified time: 2026-03-27 11:30:44
*/
import { type FC, useEffect, useState, useMemo } from "react";
import clsx from 'clsx'
@@ -431,7 +431,7 @@ const Properties: FC<PropertiesProps> = ({
}
return (
<div className={clsx("rb:w-90 rb:fixed rb:right-2.5 rb:top-18.5 rb:bottom-2.5 rb:z-1000", styles.properties)}>
<div className={clsx("rb:h-[calc(100vh-88px)] rb:w-90 rb:fixed rb:right-2.5 rb:top-18.5 rb:bottom-2.5 rb:z-1000", styles.properties)}>
<RbCard
title={t('workflow.nodeProperties')}
extra={<Space>
@@ -452,7 +452,7 @@ const Properties: FC<PropertiesProps> = ({
headerType="borderless"
headerClassName={clsx("rb:font-[MiSans-Bold] rb:font-bold rb:min-h-[48px]!")}
className="rb:h-full! rb:hover:shadow-none!"
bodyClassName={clsx('rb:overflow-y-auto! rb:h-[calc(100vh-131px)]! rb:px-3! rb:pt-0! rb:pb-3!')}
bodyClassName={clsx('rb:overflow-y-auto! rb:h-[calc(100%-48px)]! rb:px-3! rb:pt-0! rb:pb-3!')}
>
<Form key={selectedNode?.getData()?.id} form={form} size="small" layout="vertical">
<Form.Item name="name" label={t('workflow.nodeName')}>

View File

@@ -100,6 +100,7 @@ export const useWorkflowGraph = ({
const [isHandMode, setIsHandMode] = useState(true);
const [config, setConfig] = useState<WorkflowConfig | null>(null);
const [chatVariables, setChatVariables] = useState<ChatVariable[]>([])
const featuresRef = useRef<FeaturesConfigForm | undefined>(undefined)
useEffect(() => {
getConfig()
@@ -121,6 +122,7 @@ export const useWorkflowGraph = ({
})
setChatVariables(initChatVariables)
setConfig({ ...rest, variables: initChatVariables })
featuresRef.current = rest.features
onFeaturesLoad?.(rest.features)
})
}
@@ -1016,6 +1018,7 @@ export const useWorkflowGraph = ({
const params = {
...config,
features: featuresRef.current,
variables: chatVariables.map(v => {
const { defaultValue, ...cleanV } = v
return {
@@ -1208,7 +1211,7 @@ export const useWorkflowGraph = ({
});
}
const handleSaveFeaturesConfig = (value?: FeaturesConfigForm) => {
setConfig(prev => prev ? { ...prev, features: value } as WorkflowConfig : prev)
featuresRef.current = value
}
return {

View File

@@ -60,7 +60,7 @@ const Workflow = forwardRef<WorkflowRef, { onFeaturesLoad?: (features: FeaturesC
handleSaveFeaturesConfig
}))
return (
<div className="rb:h-[calc(100vh-64px)] rb:relative">
<div className="rb:h-full rb:relative">
{/* 左侧节点面板 */}
<NodeLibrary collapsed={collapsed} handleToggle={handleToggle} />