mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-13 04:57:28 +00:00
add chat plugin and split query to parse and execute (#25)
* [feature](webapp) add drill down dimensions and metric period compare and modify layout * [feature](webapp) add drill down dimensions and metric period compare and modify layout * [feature](webapp) gitignore add supersonic-webapp * [feature](webapp) gitignore add supersonic-webapp * [feature](webapp) add chat plugin and split query to parse and execute * [feature](webapp) add chat plugin and split query to parse and execute * [feature](webapp) add chat plugin and split query to parse and execute --------- Co-authored-by: williamhliu <williamhliu@tencent.com>
This commit is contained in:
244
webapp/packages/supersonic-fe/src/pages/Chat/Conversation.tsx
Normal file
244
webapp/packages/supersonic-fe/src/pages/Chat/Conversation.tsx
Normal file
@@ -0,0 +1,244 @@
|
||||
import IconFont from '@/components/IconFont';
|
||||
import { Dropdown, Input, Menu } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
useEffect,
|
||||
useState,
|
||||
forwardRef,
|
||||
ForwardRefRenderFunction,
|
||||
useImperativeHandle,
|
||||
} from 'react';
|
||||
import { useLocation } from 'umi';
|
||||
import ConversationModal from './components/ConversationModal';
|
||||
import { deleteConversation, getAllConversations, saveConversation } from './service';
|
||||
import styles from './style.less';
|
||||
import { ConversationDetailType, DefaultEntityType } from './type';
|
||||
import { DEFAULT_CONVERSATION_NAME } from './constants';
|
||||
import moment from 'moment';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
|
||||
type Props = {
|
||||
currentConversation?: ConversationDetailType;
|
||||
collapsed?: boolean;
|
||||
isCopilotMode?: boolean;
|
||||
defaultDomainName?: string;
|
||||
defaultEntityFilter?: DefaultEntityType;
|
||||
triggerNewConversation?: boolean;
|
||||
onNewConversationTriggered?: () => void;
|
||||
onSelectConversation: (
|
||||
conversation: ConversationDetailType,
|
||||
name?: string,
|
||||
domainId?: number,
|
||||
entityId?: string,
|
||||
) => void;
|
||||
};
|
||||
|
||||
const Conversation: ForwardRefRenderFunction<any, Props> = (
|
||||
{
|
||||
currentConversation,
|
||||
collapsed,
|
||||
isCopilotMode,
|
||||
defaultDomainName,
|
||||
defaultEntityFilter,
|
||||
triggerNewConversation,
|
||||
onNewConversationTriggered,
|
||||
onSelectConversation,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const location = useLocation();
|
||||
const { q, cid, domainId, entityId } = (location as any).query;
|
||||
const [conversations, setConversations] = useState<ConversationDetailType[]>([]);
|
||||
const [editModalVisible, setEditModalVisible] = useState(false);
|
||||
const [editConversation, setEditConversation] = useState<ConversationDetailType>();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
updateData,
|
||||
onAddConversation,
|
||||
}));
|
||||
|
||||
const updateData = async () => {
|
||||
const { data } = await getAllConversations();
|
||||
const conversationList = data || [];
|
||||
setConversations(conversationList.slice(0, 500));
|
||||
return conversationList;
|
||||
};
|
||||
|
||||
const initData = async () => {
|
||||
const data = await updateData();
|
||||
if (data.length > 0) {
|
||||
const chatId = localStorage.getItem('CONVERSATION_ID') || cid;
|
||||
if (chatId) {
|
||||
const conversation = data.find((item: any) => item.chatId === +chatId);
|
||||
if (conversation) {
|
||||
onSelectConversation(conversation);
|
||||
} else {
|
||||
onSelectConversation(data[0]);
|
||||
}
|
||||
} else {
|
||||
onSelectConversation(data[0]);
|
||||
}
|
||||
} else {
|
||||
onAddConversation();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (triggerNewConversation) {
|
||||
const conversationName =
|
||||
defaultEntityFilter?.entityName && window.location.pathname.includes('detail')
|
||||
? defaultEntityFilter.entityName
|
||||
: defaultDomainName;
|
||||
onAddConversation({ name: conversationName, type: 'CUSTOMIZE' });
|
||||
onNewConversationTriggered?.();
|
||||
}
|
||||
}, [triggerNewConversation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (triggerNewConversation) {
|
||||
return;
|
||||
}
|
||||
if (q && cid === undefined && window.location.href.includes('/workbench/chat')) {
|
||||
onAddConversation({ name: q, domainId: domainId ? +domainId : undefined, entityId });
|
||||
} else {
|
||||
initData();
|
||||
}
|
||||
}, [q]);
|
||||
|
||||
const addConversation = async (name?: string) => {
|
||||
await saveConversation(name || DEFAULT_CONVERSATION_NAME);
|
||||
return updateData();
|
||||
};
|
||||
|
||||
const onDeleteConversation = async (id: number) => {
|
||||
await deleteConversation(id);
|
||||
initData();
|
||||
};
|
||||
|
||||
const onAddConversation = async ({
|
||||
name,
|
||||
domainId,
|
||||
entityId,
|
||||
type,
|
||||
}: {
|
||||
name?: string;
|
||||
domainId?: number;
|
||||
entityId?: string;
|
||||
type?: string;
|
||||
} = {}) => {
|
||||
const data = await addConversation(name);
|
||||
onSelectConversation(data[0], type || name, domainId, entityId);
|
||||
};
|
||||
|
||||
const onOperate = (key: string, conversation: ConversationDetailType) => {
|
||||
if (key === 'editName') {
|
||||
setEditConversation(conversation);
|
||||
setEditModalVisible(true);
|
||||
} else if (key === 'delete') {
|
||||
onDeleteConversation(conversation.chatId);
|
||||
}
|
||||
};
|
||||
|
||||
const conversationClass = classNames(styles.conversation, {
|
||||
[styles.collapsed]: collapsed,
|
||||
[styles.copilotMode]: isCopilotMode,
|
||||
});
|
||||
|
||||
const convertTime = (date: string) => {
|
||||
moment.locale('zh-cn');
|
||||
const now = moment();
|
||||
const inputDate = moment(date);
|
||||
const diffMinutes = now.diff(inputDate, 'minutes');
|
||||
if (diffMinutes < 1) {
|
||||
return '刚刚';
|
||||
} else if (inputDate.isSame(now, 'day')) {
|
||||
return inputDate.format('HH:mm');
|
||||
} else if (inputDate.isSame(now.subtract(1, 'day'), 'day')) {
|
||||
return '昨天';
|
||||
}
|
||||
return inputDate.format('MM/DD');
|
||||
};
|
||||
|
||||
const onSearchValueChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchValue(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={conversationClass}>
|
||||
<div className={styles.leftSection}>
|
||||
<div className={styles.searchConversation}>
|
||||
<Input
|
||||
placeholder="搜索"
|
||||
prefix={<SearchOutlined className={styles.searchIcon} />}
|
||||
className={styles.searchTask}
|
||||
value={searchValue}
|
||||
onChange={onSearchValueChange}
|
||||
allowClear
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.conversationList}>
|
||||
{conversations
|
||||
.filter(
|
||||
(conversation) =>
|
||||
searchValue === '' ||
|
||||
conversation.chatName.toLowerCase().includes(searchValue.toLowerCase()),
|
||||
)
|
||||
.map((item) => {
|
||||
const conversationItemClass = classNames(styles.conversationItem, {
|
||||
[styles.activeConversationItem]: currentConversation?.chatId === item.chatId,
|
||||
});
|
||||
return (
|
||||
<Dropdown
|
||||
key={item.chatId}
|
||||
overlay={
|
||||
<Menu
|
||||
items={[
|
||||
{ label: '修改对话名称', key: 'editName' },
|
||||
{ label: '删除', key: 'delete' },
|
||||
]}
|
||||
onClick={({ key }) => {
|
||||
onOperate(key, item);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
trigger={['contextMenu']}
|
||||
>
|
||||
<div
|
||||
className={conversationItemClass}
|
||||
onClick={() => {
|
||||
onSelectConversation(item);
|
||||
}}
|
||||
>
|
||||
<IconFont type="icon-chat1" className={styles.conversationIcon} />
|
||||
<div className={styles.conversationContent}>
|
||||
<div className={styles.topTitleBar}>
|
||||
<div className={styles.conversationName}>{item.chatName}</div>
|
||||
<div className={styles.conversationTime}>
|
||||
{convertTime(item.lastTime || '')}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.subTitle}>{item.lastQuestion}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dropdown>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<ConversationModal
|
||||
visible={editModalVisible}
|
||||
editConversation={editConversation}
|
||||
onClose={() => {
|
||||
setEditModalVisible(false);
|
||||
}}
|
||||
onFinish={() => {
|
||||
setEditModalVisible(false);
|
||||
updateData();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default forwardRef(Conversation);
|
||||
Reference in New Issue
Block a user