mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-13 21:17:08 +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}
|
className={styles.composerInput}
|
||||||
placeholder={
|
placeholder={
|
||||||
currentAgent
|
currentAgent
|
||||||
? `智能助理${
|
? `【${currentAgent.name}】将与您对话,点击${!isMobile ? '左侧' : ''}【智能助理】${
|
||||||
isMobile ? `[${currentAgent?.name}]` : `【${currentAgent?.name}】`
|
!isMobile ? '列表' : ''
|
||||||
}将与您对话,输入“/”可切换助理`
|
}可切换`
|
||||||
: '请输入您的问题'
|
: '请输入您的问题'
|
||||||
}
|
}
|
||||||
value={inputMsg}
|
value={inputMsg}
|
||||||
|
|||||||
@@ -9,18 +9,19 @@ import { CheckCircleFilled, UpOutlined } from '@ant-design/icons';
|
|||||||
import { SqlInfoType } from '../../common/type';
|
import { SqlInfoType } from '../../common/type';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
llmReq?: any;
|
||||||
integrateSystem?: string;
|
integrateSystem?: string;
|
||||||
sqlInfo: SqlInfoType;
|
sqlInfo: SqlInfoType;
|
||||||
sqlTimeCost?: number;
|
sqlTimeCost?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo, sqlTimeCost }) => {
|
const SqlItem: React.FC<Props> = ({ llmReq, integrateSystem, sqlInfo, sqlTimeCost }) => {
|
||||||
const [sqlType, setSqlType] = useState('');
|
const [sqlType, setSqlType] = useState('');
|
||||||
|
|
||||||
const tipPrefixCls = `${PREFIX_CLS}-item`;
|
const tipPrefixCls = `${PREFIX_CLS}-item`;
|
||||||
const prefixCls = `${PREFIX_CLS}-sql-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);
|
result ? message.success('复制SQL成功', 1) : message.error('复制SQL失败', 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -28,10 +29,12 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo, sqlTimeCost }) =>
|
|||||||
setSqlType('');
|
setSqlType('');
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!sqlInfo.s2SQL && !sqlInfo.correctS2SQL && !sqlInfo.querySQL) {
|
if (!llmReq && !sqlInfo.s2SQL && !sqlInfo.correctS2SQL && !sqlInfo.querySQL) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { schema, linking, priorExts } = llmReq || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${tipPrefixCls}-parse-tip`}>
|
<div className={`${tipPrefixCls}-parse-tip`}>
|
||||||
<div className={`${tipPrefixCls}-title-bar`}>
|
<div className={`${tipPrefixCls}-title-bar`}>
|
||||||
@@ -49,6 +52,18 @@ const SqlItem: React.FC<Props> = ({ integrateSystem, sqlInfo, sqlTimeCost }) =>
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={`${tipPrefixCls}-content-options`}>
|
<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 && (
|
{sqlInfo.s2SQL && (
|
||||||
<div
|
<div
|
||||||
className={`${tipPrefixCls}-content-option ${
|
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] && (
|
{sqlType && sqlInfo[sqlType] && (
|
||||||
<>
|
<>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ const ChatItem: React.FC<Props> = ({
|
|||||||
<>
|
<>
|
||||||
{!isMobile && parseInfo?.sqlInfo && isDeveloper && integrateSystem !== 'c2' && (
|
{!isMobile && parseInfo?.sqlInfo && isDeveloper && integrateSystem !== 'c2' && (
|
||||||
<SqlItem
|
<SqlItem
|
||||||
|
llmReq={parseInfo?.properties?.CONTEXT?.llmReq}
|
||||||
integrateSystem={integrateSystem}
|
integrateSystem={integrateSystem}
|
||||||
sqlInfo={parseInfo.sqlInfo}
|
sqlInfo={parseInfo.sqlInfo}
|
||||||
sqlTimeCost={parseTimeCost?.sqlTime}
|
sqlTimeCost={parseTimeCost?.sqlTime}
|
||||||
|
|||||||
@@ -447,6 +447,26 @@
|
|||||||
color: var(--chat-blue);
|
color: var(--chat-blue);
|
||||||
cursor: pointer;
|
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 {
|
.@{sql-item-prefix-cls}-copilot {
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { DeleteOutlined, EditOutlined, PlusOutlined, UserOutlined } from '@ant-design/icons';
|
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 classNames from 'classnames';
|
||||||
|
import moment from 'moment';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import styles from './style.less';
|
import styles from './style.less';
|
||||||
import { AgentType } from './type';
|
import { AgentType } from './type';
|
||||||
|
|
||||||
const { Search } = Input;
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
agents: AgentType[];
|
agents: AgentType[];
|
||||||
currentAgent?: AgentType;
|
currentAgent?: AgentType;
|
||||||
@@ -25,33 +24,108 @@ const AgentsSection: React.FC<Props> = ({
|
|||||||
onEditAgent,
|
onEditAgent,
|
||||||
onSaveAgent,
|
onSaveAgent,
|
||||||
}) => {
|
}) => {
|
||||||
// const [searchName, setSearchName] = useState('');
|
|
||||||
const [showAgents, setShowAgents] = useState<AgentType[]>([]);
|
const [showAgents, setShowAgents] = useState<AgentType[]>([]);
|
||||||
|
const [showType, setShowType] = useState(localStorage.getItem('AGENT_SHOW_TYPE') || 'list');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShowAgents(agents);
|
setShowAgents(agents);
|
||||||
}, [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 (
|
return (
|
||||||
<div className={styles.agentsSection}>
|
<div className={styles.agentsSection}>
|
||||||
{/* <div className={styles.sectionTitle}>问答助理</div> */}
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.searchBar}>
|
<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
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -61,7 +135,22 @@ const AgentsSection: React.FC<Props> = ({
|
|||||||
<PlusOutlined />
|
<PlusOutlined />
|
||||||
新建助理
|
新建助理
|
||||||
</Button>
|
</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>
|
||||||
|
{showType === 'list' ? (
|
||||||
|
<Table columns={columns} dataSource={showAgents} />
|
||||||
|
) : (
|
||||||
<div className={styles.agentsContainer}>
|
<div className={styles.agentsContainer}>
|
||||||
{showAgents.map((agent) => {
|
{showAgents.map((agent) => {
|
||||||
const agentItemClass = classNames(styles.agentItem, {
|
const agentItemClass = classNames(styles.agentItem, {
|
||||||
@@ -136,6 +225,7 @@ const AgentsSection: React.FC<Props> = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
.agent {
|
.agent {
|
||||||
// background: #fff;
|
|
||||||
height: calc(100vh - 48px);
|
height: calc(100vh - 48px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@
|
|||||||
.searchBar {
|
.searchBar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 20px;
|
column-gap: 30px;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
|
|
||||||
.searchControl {
|
.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