feat(web): app page ui upgrade
This commit is contained in:
@@ -2,13 +2,12 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:29:21
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-03 14:24:34
|
||||
* @Last Modified time: 2026-03-04 10:28:59
|
||||
*/
|
||||
import { type FC, type ReactNode, useEffect, useRef, useState, forwardRef, useImperativeHandle } from 'react';
|
||||
import clsx from 'clsx'
|
||||
import { useEffect, useRef, useState, forwardRef, useImperativeHandle } from 'react';
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Row, Col, Space, Form, Input, Switch, Button, App, Spin } from 'antd'
|
||||
import { Row, Col, Space, Form, Input, Button, App, Spin, Flex } from 'antd'
|
||||
|
||||
import Chat from './components/Chat'
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
@@ -35,92 +34,13 @@ import VariableList from './components/VariableList/VariableList'
|
||||
import { getApplicationConfig } from '@/api/application'
|
||||
import { memoryConfigListUrl } from '@/api/memory'
|
||||
import CustomSelect from '@/components/CustomSelect'
|
||||
import aiPrompt from '@/assets/images/application/aiPrompt.png'
|
||||
import AiPromptModal from './components/AiPromptModal'
|
||||
import ToolList from './components/ToolList/ToolList'
|
||||
import SkillList from './components/Skill'
|
||||
import ChatVariableConfigModal from './components/ChatVariableConfigModal';
|
||||
import type { Skill } from '@/views/Skills/types'
|
||||
|
||||
/**
|
||||
* Description wrapper component
|
||||
* @param desc - Description text
|
||||
* @param className - Additional CSS classes
|
||||
*/
|
||||
const DescWrapper: FC<{desc: string, className?: string}> = ({desc, className}) => {
|
||||
return (
|
||||
<div className={clsx(className, "rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>
|
||||
{desc}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Label wrapper component
|
||||
* @param title - Label title
|
||||
* @param className - Additional CSS classes
|
||||
* @param children - Child elements
|
||||
*/
|
||||
const LabelWrapper: FC<{title: string, className?: string; children?: ReactNode}> = ({title, className, children}) => {
|
||||
return (
|
||||
<div className={clsx(className, "rb:text-[14px] rb:font-medium rb:leading-5")}>
|
||||
{title}
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Switch wrapper component with label and description
|
||||
* @param title - Switch title
|
||||
* @param desc - Optional description
|
||||
* @param name - Form field name
|
||||
* @param needTransition - Whether to translate text
|
||||
*/
|
||||
const SwitchWrapper: FC<{ title: string, desc?: string, name: string | string[]; needTransition?: boolean; }> = ({ title, desc, name, needTransition = true }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="rb:flex rb:items-center rb:justify-between">
|
||||
<LabelWrapper title={needTransition ? t(`application.${title}`) : title}>
|
||||
{desc && <DescWrapper desc={needTransition ? t(`application.${desc}`) : desc} className="rb:mt-2" />}
|
||||
</LabelWrapper>
|
||||
<Form.Item
|
||||
name={name}
|
||||
valuePropName="checked"
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Select wrapper component with label and description
|
||||
* @param title - Select title
|
||||
* @param desc - Description text
|
||||
* @param name - Form field name
|
||||
* @param url - API URL for options
|
||||
*/
|
||||
const SelectWrapper: FC<{ title: string, desc: string, name: string | string[], url: string }> = ({ title, desc, name, url }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<LabelWrapper title={t(`application.${title}`)} className="rb:mb-2">
|
||||
</LabelWrapper>
|
||||
<Form.Item
|
||||
name={name}
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<CustomSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
url={url}
|
||||
hasAll={false}
|
||||
valueKey='config_id'
|
||||
labelKey="config_name"
|
||||
/>
|
||||
</Form.Item>
|
||||
<DescWrapper desc={t(`application.${desc}`)} className="rb:mt-2" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||
import DescWrapper from '@/components/FormItem/DescWrapper'
|
||||
|
||||
/**
|
||||
* Agent configuration component
|
||||
@@ -172,8 +92,8 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
const allSkills = Array.isArray(skills?.skill_ids) ? skills?.skill_ids.map(vo => ({ id: vo })) : []
|
||||
const allTools = Array.isArray(response.tools) ? response.tools : []
|
||||
const memoryContent = response.memory?.memory_config_id
|
||||
const parsedMemoryContent = memoryContent === null || memoryContent === ''
|
||||
? undefined
|
||||
const parsedMemoryContent = memoryContent === null || memoryContent === ''
|
||||
? undefined
|
||||
: !isNaN(Number(memoryContent)) ? Number(memoryContent) : memoryContent
|
||||
const variableList = variables?.map((item, index) => ({
|
||||
...item,
|
||||
@@ -410,46 +330,48 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
return (
|
||||
<>
|
||||
{loading && <Spin fullscreen></Spin>}
|
||||
<Row className="rb:h-[calc(100vh-64px)]">
|
||||
<Col span={12} className="rb:h-full rb:overflow-x-auto rb:border-r rb:border-[#DFE4ED] rb:p-[20px_16px_24px_16px]">
|
||||
<div className="rb:flex rb:items-center rb:justify-end rb:mb-5">
|
||||
<Space size={10}>
|
||||
<Button onClick={handleModelConfig} className="rb:group">
|
||||
{defaultModel?.name ? <div className="rb:w-4 rb:h-4 rb:bg-[url('@/assets/images/application/model.svg')] rb:group-hover:bg-[url('@/assets/images/application/model_hover.svg')]"></div> : null}
|
||||
{defaultModel?.name || t('application.chooseModel')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={() => handleSave()}>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<Row className="rb:h-[calc(100vh-88px)]" gutter={12}>
|
||||
<Col span={12} className="rb:h-full rb:overflow-y-auto">
|
||||
<Form form={form}>
|
||||
<Form.Item name="default_model_config_id" hidden noStyle></Form.Item>
|
||||
<Form.Item name="model_parameters" hidden noStyle></Form.Item>
|
||||
<Space size={16} direction="vertical" style={{ width: '100%' }}>
|
||||
<Card title={t('application.promptConfiguration')}>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-2.75">
|
||||
<div className="rb:font-medium rb:leading-5">
|
||||
{t('application.configuration')}
|
||||
<span className="rb:font-regular rb:text-[12px] rb:text-[#5B6167]"> ({t('application.configurationDesc')})</span>
|
||||
</div>
|
||||
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handlePrompt}>
|
||||
<img src={aiPrompt} className="rb:size-5" />
|
||||
{t('application.aiPrompt')}
|
||||
</Button>
|
||||
<Flex gap={16} 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">
|
||||
{defaultModel?.name ? <div className="rb:size-4 rb:bg-[url('@/assets/images/application/model.svg')]"></div> : null}
|
||||
{defaultModel?.name || t('application.chooseModel')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={() => handleSave()}>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Form.Item name="default_model_config_id" hidden noStyle></Form.Item>
|
||||
<Form.Item name="model_parameters" 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!"
|
||||
rules={[{ max: 10000 }]}
|
||||
>
|
||||
<Form.Item name="system_prompt" className="rb:mb-0!">
|
||||
<Input.TextArea
|
||||
placeholder={t('application.promptPlaceholder')}
|
||||
styles={{
|
||||
textarea: {
|
||||
minHeight: '200px',
|
||||
borderRadius: '8px'
|
||||
borderRadius: '8px',
|
||||
padding: '12px'
|
||||
},
|
||||
}}
|
||||
/>
|
||||
@@ -462,15 +384,28 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
|
||||
{/* Memory Configuration */}
|
||||
<Card title={t('application.memoryConfiguration')}>
|
||||
<Space size={24} direction='vertical' style={{ width: '100%' }}>
|
||||
<SwitchWrapper title="dialogueHistoricalMemory" desc="dialogueHistoricalMemoryDesc" name={['memory', 'enabled']} />
|
||||
<SelectWrapper
|
||||
title="selectMemoryContent"
|
||||
desc="selectMemoryContentDesc"
|
||||
name={['memory', 'memory_config_id']}
|
||||
url={memoryConfigListUrl}
|
||||
<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')}
|
||||
/>
|
||||
</Space>
|
||||
<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"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
||||
<Form.Item name="variables" noStyle>
|
||||
@@ -485,26 +420,29 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
|
||||
<Form.Item name="tools" noStyle>
|
||||
<ToolList />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
</Flex>
|
||||
</Form>
|
||||
</Col>
|
||||
<Col span={12} className="rb:h-full rb:overflow-x-hidden rb:p-[20px_16px_24px_16px]">
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:mb-5">
|
||||
{t('application.debuggingAndPreview')}
|
||||
|
||||
<Space size={10}>
|
||||
{chatVariables.length > 0 &&
|
||||
<Col span={12} className="rb:h-full rb:overflow-y-hidden">
|
||||
<RbCard
|
||||
title={t('application.debuggingAndPreview')}
|
||||
extra={
|
||||
<Space size={10}>
|
||||
<Button type="primary" ghost onClick={handleOpenVariableConfig}>
|
||||
{t('application.variableConfig')}
|
||||
</Button>
|
||||
}
|
||||
<Button type="primary" ghost onClick={handleAddModel}>
|
||||
+ {t('application.addModel')}
|
||||
</Button>
|
||||
<div className="rb:w-8 rb:h-8 rb:cursor-pointer rb:bg-[url('@/assets/images/application/clean.svg')]" onClick={handleClearDebugging}></div>
|
||||
</Space>
|
||||
</div>
|
||||
<RbCard height="calc(100vh - 160px)" bodyClassName="rb:p-[0]! rb:h-full rb:overflow-hidden">
|
||||
<Button type="primary" ghost onClick={handleAddModel}>
|
||||
+ {t('application.addModel')}
|
||||
</Button>
|
||||
<div className="rb:w-8 rb:h-8 rb:cursor-pointer rb:bg-[url('@/assets/images/application/clean.svg')]" onClick={handleClearDebugging}></div>
|
||||
</Space>
|
||||
}
|
||||
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"
|
||||
>
|
||||
<Chat
|
||||
data={data as Config}
|
||||
chatList={chatList}
|
||||
|
||||
Reference in New Issue
Block a user