mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-11 03:58:14 +00:00
(feature)(webapp) agent defaults to list mode, supports switching to card mode (#362)
This commit is contained in:
@@ -328,9 +328,9 @@ const ChatFooter: ForwardRefRenderFunction<any, Props> = (
|
||||
className={styles.composerInput}
|
||||
placeholder={
|
||||
currentAgent
|
||||
? `智能助理${
|
||||
isMobile ? `[${currentAgent?.name}]` : `【${currentAgent?.name}】`
|
||||
}将与您对话,输入“/”可切换助理`
|
||||
? `【${currentAgent.name}】将与您对话,点击${!isMobile ? '左侧' : ''}【智能助理】${
|
||||
!isMobile ? '列表' : ''
|
||||
}可切换`
|
||||
: '请输入您的问题'
|
||||
}
|
||||
value={inputMsg}
|
||||
|
||||
@@ -9,18 +9,19 @@ import { CheckCircleFilled, UpOutlined } from '@ant-design/icons';
|
||||
import { SqlInfoType } from '../../common/type';
|
||||
|
||||
type Props = {
|
||||
llmReq?: any;
|
||||
integrateSystem?: string;
|
||||
sqlInfo: SqlInfoType;
|
||||
sqlTimeCost?: number;
|
||||
};
|
||||
|
||||
const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo, sqlTimeCost }) => {
|
||||
const SqlItem: React.FC<Props> = ({ llmReq, integrateSystem, sqlInfo, sqlTimeCost }) => {
|
||||
const [sqlType, setSqlType] = useState('');
|
||||
|
||||
const tipPrefixCls = `${PREFIX_CLS}-item`;
|
||||
const prefixCls = `${PREFIX_CLS}-sql-item`;
|
||||
|
||||
const handleCopy = (text, result) => {
|
||||
const handleCopy = (_: string, result: any) => {
|
||||
result ? message.success('复制SQL成功', 1) : message.error('复制SQL失败', 1);
|
||||
};
|
||||
|
||||
@@ -28,10 +29,12 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo, sqlTimeCost }) =>
|
||||
setSqlType('');
|
||||
};
|
||||
|
||||
if (!sqlInfo.s2SQL && !sqlInfo.correctS2SQL && !sqlInfo.querySQL) {
|
||||
if (!llmReq && !sqlInfo.s2SQL && !sqlInfo.correctS2SQL && !sqlInfo.querySQL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { schema, linking, priorExts } = llmReq || {};
|
||||
|
||||
return (
|
||||
<div className={`${tipPrefixCls}-parse-tip`}>
|
||||
<div className={`${tipPrefixCls}-title-bar`}>
|
||||
@@ -49,6 +52,18 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo, sqlTimeCost }) =>
|
||||
)}
|
||||
</div>
|
||||
<div className={`${tipPrefixCls}-content-options`}>
|
||||
{llmReq && (
|
||||
<div
|
||||
className={`${tipPrefixCls}-content-option ${
|
||||
sqlType === 'schemaMap' ? `${tipPrefixCls}-content-option-active` : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSqlType(sqlType === 'schemaMap' ? '' : 'schemaMap');
|
||||
}}
|
||||
>
|
||||
Schema映射
|
||||
</div>
|
||||
)}
|
||||
{sqlInfo.s2SQL && (
|
||||
<div
|
||||
className={`${tipPrefixCls}-content-option ${
|
||||
@@ -96,6 +111,38 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo, sqlTimeCost }) =>
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
{sqlType === 'schemaMap' && (
|
||||
<div className={`${prefixCls}-code`}>
|
||||
{schema?.fieldNameList?.length > 0 && (
|
||||
<div className={`${prefixCls}-schema-row`}>
|
||||
<div className={`${prefixCls}-schema-title`}>名称:</div>
|
||||
<div className={`${prefixCls}-schema-content`}>
|
||||
{schema.fieldNameList.join('、')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{linking?.length > 0 && (
|
||||
<div className={`${prefixCls}-schema-row`}>
|
||||
<div className={`${prefixCls}-schema-title`}>取值:</div>
|
||||
<div className={`${prefixCls}-schema-content`}>
|
||||
{linking.map((item: any) => {
|
||||
return (
|
||||
<span>
|
||||
{item.fieldName}: {item.fieldValue}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{priorExts && (
|
||||
<div className={`${prefixCls}-schema-row`}>
|
||||
<div className={`${prefixCls}-schema-title`}>附加:</div>
|
||||
<div className={`${prefixCls}-schema-content`}>{priorExts}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{sqlType && sqlInfo[sqlType] && (
|
||||
<>
|
||||
<SyntaxHighlighter
|
||||
|
||||
@@ -307,6 +307,7 @@ const ChatItem: React.FC<Props> = ({
|
||||
<>
|
||||
{!isMobile && parseInfo?.sqlInfo && isDeveloper && integrateSystem !== 'c2' && (
|
||||
<SqlItem
|
||||
llmReq={parseInfo?.properties?.CONTEXT?.llmReq}
|
||||
integrateSystem={integrateSystem}
|
||||
sqlInfo={parseInfo.sqlInfo}
|
||||
sqlTimeCost={parseTimeCost?.sqlTime}
|
||||
|
||||
@@ -447,6 +447,26 @@
|
||||
color: var(--chat-blue);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&-schema-row {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-schema-title {
|
||||
width: 50px;
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&-schema-content {
|
||||
flex: 1;
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.@{sql-item-prefix-cls}-copilot {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { DeleteOutlined, EditOutlined, PlusOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import { Button, Input, Popconfirm, Switch } from 'antd';
|
||||
import { Button, Popconfirm, Switch, Table } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import moment from 'moment';
|
||||
import { useEffect, useState } from 'react';
|
||||
import styles from './style.less';
|
||||
import { AgentType } from './type';
|
||||
|
||||
const { Search } = Input;
|
||||
|
||||
type Props = {
|
||||
agents: AgentType[];
|
||||
currentAgent?: AgentType;
|
||||
@@ -25,33 +24,108 @@ const AgentsSection: React.FC<Props> = ({
|
||||
onEditAgent,
|
||||
onSaveAgent,
|
||||
}) => {
|
||||
// const [searchName, setSearchName] = useState('');
|
||||
const [showAgents, setShowAgents] = useState<AgentType[]>([]);
|
||||
const [showType, setShowType] = useState(localStorage.getItem('AGENT_SHOW_TYPE') || 'list');
|
||||
|
||||
useEffect(() => {
|
||||
setShowAgents(agents);
|
||||
}, [agents]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '助理名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (value: string, agent: AgentType) => {
|
||||
return (
|
||||
<a
|
||||
onClick={() => {
|
||||
onSelectAgent(agent);
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
render: (status: number, agent: AgentType) => {
|
||||
return (
|
||||
<div className={styles.toggleStatus}>
|
||||
{status === 0 ? '已禁用' : <span className={styles.online}>已启用</span>}
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Switch
|
||||
size="small"
|
||||
defaultChecked={status === 1}
|
||||
onChange={(value) => {
|
||||
onSaveAgent({ ...agent, status: value ? 1 : 0 }, true);
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '更新人',
|
||||
dataIndex: 'updatedBy',
|
||||
key: 'updatedBy',
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
dataIndex: 'updatedAt',
|
||||
key: 'updatedAt',
|
||||
render: (value: string) => {
|
||||
return moment(value).format('YYYY-MM-DD HH:mm:ss');
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'x',
|
||||
key: 'x',
|
||||
render: (_: any, agent: AgentType) => {
|
||||
return (
|
||||
<div className={styles.operateIcons}>
|
||||
<a
|
||||
onClick={() => {
|
||||
onSelectAgent(agent);
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</a>
|
||||
<Popconfirm
|
||||
title="确定删除吗?"
|
||||
onCancel={(e) => {
|
||||
e?.stopPropagation();
|
||||
}}
|
||||
onConfirm={() => {
|
||||
onDeleteAgent(agent.id!);
|
||||
}}
|
||||
>
|
||||
<a>删除</a>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={styles.agentsSection}>
|
||||
{/* <div className={styles.sectionTitle}>问答助理</div> */}
|
||||
<div className={styles.content}>
|
||||
<div className={styles.searchBar}>
|
||||
{/* <Search
|
||||
placeholder="请输入助理名称搜索"
|
||||
className={styles.searchControl}
|
||||
value={searchName}
|
||||
onChange={(e) => {
|
||||
setSearchName(e.target.value);
|
||||
}}
|
||||
onSearch={(value) => {
|
||||
setShowAgents(
|
||||
agents.filter((agent) =>
|
||||
agent.name?.trim().toLowerCase().includes(value.trim().toLowerCase()),
|
||||
),
|
||||
);
|
||||
}}
|
||||
/> */}
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
@@ -61,81 +135,97 @@ const AgentsSection: React.FC<Props> = ({
|
||||
<PlusOutlined />
|
||||
新建助理
|
||||
</Button>
|
||||
<div className={styles.switchShowType}>
|
||||
<span className={styles.switchShowTypeLabel}>切换为卡片</span>
|
||||
<Switch
|
||||
size="small"
|
||||
checked={showType === 'card'}
|
||||
onChange={(value) => {
|
||||
const showTypeValue = value ? 'card' : 'list';
|
||||
setShowType(showTypeValue);
|
||||
localStorage.setItem('AGENT_SHOW_TYPE', showTypeValue);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.agentsContainer}>
|
||||
{showAgents.map((agent) => {
|
||||
const agentItemClass = classNames(styles.agentItem, {
|
||||
[styles.agentActive]: agent.id === currentAgent?.id,
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={agentItemClass}
|
||||
key={agent.id}
|
||||
onClick={() => {
|
||||
onSelectAgent(agent);
|
||||
}}
|
||||
>
|
||||
<UserOutlined className={styles.agentIcon} />
|
||||
<div className={styles.agentContent}>
|
||||
<div className={styles.agentNameBar}>
|
||||
<div className={styles.agentName}>{agent.name}</div>
|
||||
<div className={styles.operateIcons}>
|
||||
<EditOutlined
|
||||
className={styles.operateIcon}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEditAgent(agent);
|
||||
}}
|
||||
/>
|
||||
<Popconfirm
|
||||
title="确定删除吗?"
|
||||
onCancel={(e) => {
|
||||
e?.stopPropagation();
|
||||
}}
|
||||
onConfirm={(e) => {
|
||||
e?.stopPropagation();
|
||||
onDeleteAgent(agent.id!);
|
||||
}}
|
||||
>
|
||||
<DeleteOutlined
|
||||
{showType === 'list' ? (
|
||||
<Table columns={columns} dataSource={showAgents} />
|
||||
) : (
|
||||
<div className={styles.agentsContainer}>
|
||||
{showAgents.map((agent) => {
|
||||
const agentItemClass = classNames(styles.agentItem, {
|
||||
[styles.agentActive]: agent.id === currentAgent?.id,
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={agentItemClass}
|
||||
key={agent.id}
|
||||
onClick={() => {
|
||||
onSelectAgent(agent);
|
||||
}}
|
||||
>
|
||||
<UserOutlined className={styles.agentIcon} />
|
||||
<div className={styles.agentContent}>
|
||||
<div className={styles.agentNameBar}>
|
||||
<div className={styles.agentName}>{agent.name}</div>
|
||||
<div className={styles.operateIcons}>
|
||||
<EditOutlined
|
||||
className={styles.operateIcon}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEditAgent(agent);
|
||||
}}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.bottomBar}>
|
||||
<div className={styles.agentDescription} title={agent.description}>
|
||||
{agent.description}
|
||||
</div>
|
||||
<div className={styles.toggleStatus}>
|
||||
{agent.status === 0 ? (
|
||||
'已禁用'
|
||||
) : (
|
||||
<span className={styles.online}>已启用</span>
|
||||
)}
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Switch
|
||||
size="small"
|
||||
defaultChecked={agent.status === 1}
|
||||
onChange={(value) => {
|
||||
onSaveAgent({ ...agent, status: value ? 1 : 0 }, true);
|
||||
<Popconfirm
|
||||
title="确定删除吗?"
|
||||
onCancel={(e) => {
|
||||
e?.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
onConfirm={(e) => {
|
||||
e?.stopPropagation();
|
||||
onDeleteAgent(agent.id!);
|
||||
}}
|
||||
>
|
||||
<DeleteOutlined
|
||||
className={styles.operateIcon}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.bottomBar}>
|
||||
<div className={styles.agentDescription} title={agent.description}>
|
||||
{agent.description}
|
||||
</div>
|
||||
<div className={styles.toggleStatus}>
|
||||
{agent.status === 0 ? (
|
||||
'已禁用'
|
||||
) : (
|
||||
<span className={styles.online}>已启用</span>
|
||||
)}
|
||||
<span
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Switch
|
||||
size="small"
|
||||
defaultChecked={agent.status === 1}
|
||||
onChange={(value) => {
|
||||
onSaveAgent({ ...agent, status: value ? 1 : 0 }, true);
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
.agent {
|
||||
// background: #fff;
|
||||
height: calc(100vh - 48px);
|
||||
}
|
||||
|
||||
@@ -20,7 +19,7 @@
|
||||
.searchBar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 20px;
|
||||
column-gap: 30px;
|
||||
margin-bottom: 40px;
|
||||
|
||||
.searchControl {
|
||||
@@ -290,3 +289,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.switchShowTypeLabel {
|
||||
color: #999;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.operateIcons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 12px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user