Merge pull request #803 from SuanmoSuanyangTechnology/feature/ui_upgrade_zy
Feature/UI upgrade zy
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 16:29:33
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-27 18:14:25
|
||||
* @Last Modified time: 2026-04-07 20:37:43
|
||||
*/
|
||||
import { useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -115,7 +115,8 @@ const Cluster = forwardRef<ClusterRef, { onFeaturesLoad?: (features: FeaturesCon
|
||||
console.log({ ids: sub_agents?.map(item => item.agent_id) })
|
||||
getApplicationList({ ids: sub_agents?.map(item => item.agent_id).join(',')})
|
||||
.then(res => {
|
||||
const applicationList = (res as Application[]) || []
|
||||
const applicationList = ((res as { items: Application[] }).items) || []
|
||||
|
||||
setSubAgents(sub_agents.map(vo => {
|
||||
const filterVO = applicationList.find(item => item.id === vo.agent_id)
|
||||
if (filterVO) {
|
||||
@@ -194,6 +195,9 @@ const Cluster = forwardRef<ClusterRef, { onFeaturesLoad?: (features: FeaturesCon
|
||||
// form.setFieldValue('features', value)
|
||||
// }
|
||||
|
||||
|
||||
console.log('subAgents', subAgents)
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && <Spin fullscreen></Spin>}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-03-13 17:27:52
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-07 18:08:18
|
||||
* @Last Modified time: 2026-04-07 20:25:45
|
||||
*/
|
||||
import { type FC, useState, useRef, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -109,7 +109,7 @@ const TestChat: FC<TestChatProps> = ({
|
||||
setFeatures(config?.features || {} as FeaturesConfigForm)
|
||||
|
||||
|
||||
if (config?.features?.opening_statement.enabled && config?.features?.opening_statement?.statement && config?.features?.opening_statement?.statement.trim() !== '') {
|
||||
if (config?.features?.opening_statement?.enabled && config?.features?.opening_statement?.statement && config?.features?.opening_statement?.statement.trim() !== '') {
|
||||
setChatList(prev => [...prev, {
|
||||
role: 'assistant',
|
||||
created_at: Date.now(),
|
||||
|
||||
@@ -562,83 +562,88 @@ const KnowledgeBaseManagement: FC = () => {
|
||||
{data.length === 0 && !loading ? (
|
||||
<Empty size={200} />
|
||||
) : (
|
||||
<div style={{ columns: '3 280px', columnGap: 12, marginBottom: 8 }}>
|
||||
{data.map((item) => {
|
||||
const modelInfo = modelMenus[item.id];
|
||||
const hasModelInfo = modelInfo && modelInfo.menu.length > 1;
|
||||
return (
|
||||
<div key={item.id} className="rb:break-inside-avoid rb:mb-3">
|
||||
<RbCard
|
||||
title={item.name}
|
||||
headerType="borderless"
|
||||
headerClassName="rb:py-3!"
|
||||
extra={
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<Dropdown
|
||||
menu={{ items: getOptMenuItems(item) }}
|
||||
placement="bottomRight"
|
||||
>
|
||||
<div onClick={(e) => e.stopPropagation()} className="rb:cursor-pointer rb:size-5.5 rb:bg-[url('@/assets/images/common/more.svg')] rb:hover:bg-[url('@/assets/images/common/more_hover.svg')]"></div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className='' onClick={() => handleToDetail(item)}>
|
||||
<div className="rb:flex rb:text-[#5B6167] rb:h-5 rb:line-clamp-1 rb:text-sm rb:leading-5 rb:mb-3">
|
||||
{/* <div className="rb:font-medium rb:w-20">{t('knowledgeBase.description')} </div> */}
|
||||
<Tooltip title={item.description}>
|
||||
<div className='rb:flex-1 rb:text-left rb:leading-5 rb:text-gray-800 rb:wrap-break-word rb:line-clamp-2'>{(item.description && item.description != '') ? item.description : t('knowledgeBase.noDescription')}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Flex vertical gap={4} className='rb:min-h-15 rb:py-2.5! rb:px-3! rb:bg-[#F6F6F6] rb:rounded-lg rb:mb-3'>
|
||||
{item.descriptionItems?.map((description: Record<string, unknown>) => (
|
||||
<div
|
||||
key={description.key as string}
|
||||
className="rb:grid rb:grid-cols-2 rb:text-[#5B6167] rb:text-[14px] rb:leading-5"
|
||||
>
|
||||
<div className={clsx('rb:whitespace-nowrap rb:w-20', {"rb:text-gray-800 rb:font-medium" : (description.key as string) === 'permission_id'})}>{(description.label as string)}</div>
|
||||
<div className={clsx('rb:flex-inline rb:text-left rb:py-px rb:rounded',{
|
||||
"rb:text-[#155eef] rb:font-medium": (description.key as string) === 'permission_id' && (description.children as string) === t('knowledgeBase.private'),
|
||||
"rb:text-[#FF8A4C] rb:font-medium": (description.key as string) === 'permission_id' && (description.children as string) === t('knowledgeBase.share'),
|
||||
})}>{(description.children as string)}</div>
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
{hasModelInfo && (
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<div
|
||||
className="rb:flex rb:items-center rb:pt-2 rb:px-2 rb:text-[12px] rb:leading-5 rb:cursor-pointer rb:rounded rb:transition-colors"
|
||||
onClick={() => {
|
||||
setData(prev => prev.map(d => d.id === item.id ? { ...d, _expanded: !d._expanded } : d));
|
||||
}}
|
||||
>
|
||||
{/* <span className='rb:text-gray-500'>{t('knowledgeBase.models')}:</span> */}
|
||||
<span className="rb:ml-1 rb:truncate rb:flex-1 rb:text-gray-500">
|
||||
{modelInfo.summary[0].split(':')[0]}:<span className="rb:text-gray-900">{modelInfo.summary[0].split(':').slice(1).join(':')}</span>
|
||||
</span>
|
||||
<span className="rb:ml-auto rb:text-gray-400 rb:text-[10px]">
|
||||
{item._expanded ? <DownOutlined /> : <RightOutlined />}
|
||||
</span>
|
||||
</div>
|
||||
{item._expanded && (
|
||||
<div className="rb:py-1 rb:px-2 rb:text-[12px]">
|
||||
{modelInfo.summary.slice(1).map((text, idx) => {
|
||||
const [label, value] = text.split(':');
|
||||
return (
|
||||
<div key={idx} className="rb:py-1 rb:text-gray-500">
|
||||
{label}:<span className="rb:text-gray-900">{value}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<Flex align="flex-start" gap={12} className="rb:mb-2!">
|
||||
{[0, 1, 2].map(colIdx => (
|
||||
<div key={colIdx} style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{data.filter((_, i) => i % 3 === colIdx).map((item) => {
|
||||
const modelInfo = modelMenus[item.id];
|
||||
const hasModelInfo = modelInfo && modelInfo.menu.length > 1;
|
||||
return (
|
||||
<div key={item.id}>
|
||||
<RbCard
|
||||
title={item.name}
|
||||
headerType="borderless"
|
||||
headerClassName="rb:py-3!"
|
||||
extra={
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<Dropdown
|
||||
menu={{ items: getOptMenuItems(item) }}
|
||||
placement="bottomRight"
|
||||
>
|
||||
<div onClick={(e) => e.stopPropagation()} className="rb:cursor-pointer rb:size-5.5 rb:bg-[url('@/assets/images/common/more.svg')] rb:hover:bg-[url('@/assets/images/common/more_hover.svg')]"></div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</RbCard>
|
||||
}
|
||||
>
|
||||
<div className='' onClick={() => handleToDetail(item)}>
|
||||
<div className="rb:flex rb:text-[#5B6167] rb:h-5 rb:line-clamp-1 rb:text-sm rb:leading-5 rb:mb-3">
|
||||
{/* <div className="rb:font-medium rb:w-20">{t('knowledgeBase.description')} </div> */}
|
||||
<Tooltip title={item.description}>
|
||||
<div className='rb:flex-1 rb:text-left rb:leading-5 rb:text-gray-800 rb:wrap-break-word rb:line-clamp-2'>{(item.description && item.description != '') ? item.description : t('knowledgeBase.noDescription')}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Flex vertical gap={4} className='rb:min-h-15 rb:py-2.5! rb:px-3! rb:bg-[#F6F6F6] rb:rounded-lg rb:mb-3'>
|
||||
{item.descriptionItems?.map((description: Record<string, unknown>) => (
|
||||
<div
|
||||
key={description.key as string}
|
||||
className="rb:grid rb:grid-cols-2 rb:text-[#5B6167] rb:text-[14px] rb:leading-5"
|
||||
>
|
||||
<div className={clsx('rb:whitespace-nowrap rb:w-20', {"rb:text-gray-800 rb:font-medium" : (description.key as string) === 'permission_id'})}>{(description.label as string)}</div>
|
||||
<div className={clsx('rb:flex-inline rb:text-left rb:py-px rb:rounded',{
|
||||
"rb:text-[#155eef] rb:font-medium": (description.key as string) === 'permission_id' && (description.children as string) === t('knowledgeBase.private'),
|
||||
"rb:text-[#FF8A4C] rb:font-medium": (description.key as string) === 'permission_id' && (description.children as string) === t('knowledgeBase.share'),
|
||||
})}>{(description.children as string)}</div>
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
{hasModelInfo && (
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<div
|
||||
className="rb:flex rb:items-center rb:pt-2 rb:px-2 rb:text-[12px] rb:leading-5 rb:cursor-pointer rb:rounded rb:transition-colors"
|
||||
onClick={() => {
|
||||
setData(prev => prev.map(d => d.id === item.id ? { ...d, _expanded: !d._expanded } : d));
|
||||
}}
|
||||
>
|
||||
{/* <span className='rb:text-gray-500'>{t('knowledgeBase.models')}:</span> */}
|
||||
<span className="rb:ml-1 rb:truncate rb:flex-1 rb:text-gray-500">
|
||||
{modelInfo.summary[0].split(':')[0]}:<span className="rb:text-gray-900">{modelInfo.summary[0].split(':').slice(1).join(':')}</span>
|
||||
</span>
|
||||
<span className="rb:ml-auto rb:text-gray-400 rb:text-[10px]">
|
||||
{item._expanded ? <DownOutlined /> : <RightOutlined />}
|
||||
</span>
|
||||
</div>
|
||||
{item._expanded && (
|
||||
<div className="rb:py-1 rb:px-2 rb:text-[12px]">
|
||||
{modelInfo.summary.slice(1).map((text, idx) => {
|
||||
const [label, value] = text.split(':');
|
||||
return (
|
||||
<div key={idx} className="rb:py-1 rb:text-gray-500">
|
||||
{label}:<span className="rb:text-gray-900">{value}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</RbCard>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)})}
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
</InfiniteScroll>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2025-12-30 13:59:36
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-02 19:01:12
|
||||
* @Last Modified time: 2026-04-07 20:17:59
|
||||
*/
|
||||
import { forwardRef, useImperativeHandle, useState, useRef, useMemo } from 'react';
|
||||
import { Form, Input, Select, InputNumber, Button, Row, Col, Flex, Spin } from 'antd';
|
||||
@@ -135,7 +135,10 @@ const ChatVariableModal = forwardRef<ChatVariableModalRef, ChatVariableModalProp
|
||||
|
||||
const handleSave = () => {
|
||||
form.validateFields().then((values) => {
|
||||
refresh({ ...values, default: values.defaultValue }, editIndex);
|
||||
const defaultValue = Array.isArray(values.defaultValue)
|
||||
? values.defaultValue.filter((v: any) => v !== undefined && v !== null && v !== '')
|
||||
: values.defaultValue;
|
||||
refresh({ ...values, defaultValue }, editIndex);
|
||||
handleClose();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 15:40:13
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-03 20:19:34
|
||||
* @Last Modified time: 2026-04-07 19:40:27
|
||||
*/
|
||||
import { useState, useRef, useEffect, useLayoutEffect, type FC } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
@@ -232,15 +232,16 @@ const VariableSelect: FC<VariableSelectProps> = ({
|
||||
<span className="rb:text-[#bfbfbf] rb:flex-1 rb:text-[12px]">{placeholder}</span>
|
||||
)
|
||||
) : selectedSuggestion ? (
|
||||
<span className="rb:flex rb:flex-1 rb:min-w-0">
|
||||
<span className="rb:inline-flex rb:items-center rb:gap-0.5 rb:bg-[#f0f8ff] rb:rounded rb:px-1 rb:py-0.5 rb:text-[11px] rb:max-w-full">
|
||||
<div className="rb:flex rb:flex-1 rb:min-w-0 rb:max-w-full">
|
||||
<span className="rb:inline-flex rb:items-center rb:gap-0.5 rb:bg-[#f0f8ff] rb:rounded rb:px-1 rb:py-0.5 rb:text-[11px] rb:max-w-full rb:overflow-hidden">
|
||||
{!isConversation && nodeData?.icon && <div className={`rb:size-3 rb:shrink-0 rb:bg-cover ${nodeData.icon}`} />}
|
||||
{!isConversation && nodeData?.name && <span className="rb:text-[#5B6167]">{nodeData.name}{sep}</span>}
|
||||
<span className="rb:text-[#171719]">
|
||||
{!isConversation && nodeData?.name && <span className="rb:text-[#5B6167] rb:shrink rb:min-w-0 rb:truncate rb:max-w-[40%]">{nodeData.name}</span>}
|
||||
{!isConversation && nodeData?.name && <span className="rb:text-[#5B6167]">{sep}</span>}
|
||||
<span className="rb:text-[#171719] rb:shrink rb:min-w-0 rb:truncate">
|
||||
{parentOfSelected ? <>{parentOfSelected.label}{sep}{selectedSuggestion.label}</> : selectedSuggestion.label}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<span className="rb:text-[#bfbfbf] rb:flex-1">{placeholder}</span>
|
||||
)}
|
||||
@@ -264,7 +265,7 @@ const VariableSelect: FC<VariableSelectProps> = ({
|
||||
{open && createPortal(
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className="rb:fixed rb:z-9999 rb:bg-white rb:text-[14px] rb:rounded-xl rb:shadow-[0px_2px_12px_0px_rgba(23,23,25,0.12)]"
|
||||
className="rb:fixed rb:z-9999 rb:bg-white rb:text-[14px] rb:rounded-lg rb:shadow-[0px_2px_12px_0px_rgba(23,23,25,0.12)] rb:p-1"
|
||||
style={{ top: dropdownPos.top, left: dropdownPos.left, minWidth: dropdownPos.width }}
|
||||
>
|
||||
<div className="rb:min-w-70 rb:max-h-60 rb:overflow-y-auto rb:py-1">
|
||||
@@ -272,8 +273,8 @@ const VariableSelect: FC<VariableSelectProps> = ({
|
||||
const nd = suggestions[0].nodeData;
|
||||
return (
|
||||
<div key={nodeId}>
|
||||
<Flex align="center" gap={4} className="rb:px-3! rb:py-1.25! rb:text-[12px] rb:font-medium rb:text-[#5B6167]">
|
||||
{nd.icon && <div className={`rb:size-3 rb:bg-cover ${nd.icon}`} />}
|
||||
<Flex align="center" gap={4} className="rb:px-3! rb:py-1.25! rb:text-[12px] rb:text-[#5B6167]">
|
||||
{nd.icon && <div className={`rb:size-4 rb:bg-cover ${nd.icon}`} />}
|
||||
{nd.name}
|
||||
</Flex>
|
||||
{suggestions.map(s => {
|
||||
@@ -286,14 +287,15 @@ const VariableSelect: FC<VariableSelectProps> = ({
|
||||
<Flex
|
||||
key={s.key}
|
||||
ref={(el) => { if (el) itemRefs.current.set(s.key, el); }}
|
||||
className="rb:mx-3! rb:pl-3! rb:pr-3! rb:py-1.5! rb:rounded-lg!"
|
||||
className={clsx("rb:pl-6! rb:pr-3! rb:py-1.25! rb:rounded-lg!", {
|
||||
'rb:bg-[#e6f4ff]': isSelected || isExpanded,
|
||||
'rb:bg-white rb:hover:bg-[#F6F6F6]!': !(isSelected || isExpanded),
|
||||
'rb:opacity-60': s.disabled,
|
||||
'rb:cursor-not-allowed': s.disabled,
|
||||
'rb:cursor-pointer': !s.disabled,
|
||||
})}
|
||||
align="center"
|
||||
justify="space-between"
|
||||
style={{
|
||||
cursor: s.disabled ? 'not-allowed' : 'pointer',
|
||||
background: isSelected || isExpanded ? '#f0f8ff' : 'white',
|
||||
opacity: s.disabled ? 0.5 : 1,
|
||||
}}
|
||||
onClick={() => {
|
||||
if (s.disabled) return;
|
||||
if (hasChildren) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-01-19 17:00:26
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-02 16:58:40
|
||||
* @Last Modified time: 2026-04-07 20:33:26
|
||||
*/
|
||||
/**
|
||||
* useVariableList Hook
|
||||
@@ -125,7 +125,7 @@ const processNodeVariables = (
|
||||
if (type in NODE_VARIABLES) {
|
||||
if (type === 'list-operator') {
|
||||
// Determine output type from the first variable in config
|
||||
const variableValue = config?.variable;
|
||||
const variableValue = config?.input_list?.defaultValue;
|
||||
let itemType = 'string';
|
||||
if (variableValue) {
|
||||
const refVar = variableList.find(v => `{{${v.value}}}` === variableValue);
|
||||
@@ -321,7 +321,6 @@ export const getChildNodeVariables = (
|
||||
if (p?.name) addVariable(list, keys, `${nodeId}_${p.name}`, p.name, p.type || 'string', `${nodeId}.${p.name}`, nodeData);
|
||||
});
|
||||
}
|
||||
|
||||
// Add code node variables
|
||||
if (type === 'code') {
|
||||
(nodeData.config?.output_variables?.defaultValue || []).forEach((p: any) => {
|
||||
@@ -393,8 +392,18 @@ export const useVariableList = (
|
||||
// Add chat variables
|
||||
chatVariables?.forEach(v => addVariable(list, keys, `CONVERSATION_${v.name}`, v.name, v.type, `conv.${v.name}`, { type: 'CONVERSATION', name: 'CONVERSATION', icon: '' }, { group: 'CONVERSATION' }));
|
||||
|
||||
// Process each relevant node
|
||||
// Process each relevant node: non-list-operator first, then list-operator
|
||||
const listOperatorIds: string[] = [];
|
||||
relevantIds.forEach(id => {
|
||||
const node = nodes.find(n => n.id === id);
|
||||
if (!node) return;
|
||||
if (node.getData()?.type === 'list-operator') {
|
||||
listOperatorIds.push(id);
|
||||
} else {
|
||||
processNodeVariables(node.getData(), node.getData().id, list, keys);
|
||||
}
|
||||
});
|
||||
listOperatorIds.forEach(id => {
|
||||
const node = nodes.find(n => n.id === id);
|
||||
if (node) processNodeVariables(node.getData(), node.getData().id, list, keys);
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 15:06:18
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-03 20:28:08
|
||||
* @Last Modified time: 2026-04-07 19:56:56
|
||||
*/
|
||||
import LoopNode from './components/Nodes/LoopNode';
|
||||
import NormalNode from './components/Nodes/NormalNode';
|
||||
@@ -579,7 +579,7 @@ export const noteNode = {
|
||||
|
||||
export const nodeWidth = 240;
|
||||
|
||||
export const conditionNodePortItemArgsY = 60;
|
||||
export const conditionNodePortItemArgsY = 56.5;
|
||||
export const conditionNodeItemHeight = 26;
|
||||
export const conditionNodeHeight = 110;
|
||||
/**
|
||||
@@ -703,7 +703,7 @@ export const portTextAttrs = { fontSize: 12, fill: '#5B6167' }
|
||||
/**
|
||||
* Port position arguments
|
||||
*/
|
||||
export const portItemArgsY = 26;
|
||||
export const portItemArgsY = 26.5;
|
||||
export const portArgs = { x: nodeWidth, y: portItemArgsY }
|
||||
|
||||
const defaultPortGroup = {
|
||||
|
||||
@@ -108,7 +108,7 @@ export interface ChatVariable {
|
||||
required: boolean;
|
||||
description: string;
|
||||
default?: string;
|
||||
defaultValue: string;
|
||||
defaultValue: string | any[];
|
||||
}
|
||||
export interface AddChatVariableRef {
|
||||
handleOpen: (value?: ChatVariable) => void;
|
||||
|
||||
Reference in New Issue
Block a user