Integrate Chat and Copilot into chat-sdk, and add SQL parse display (#166)

This commit is contained in:
williamhliu
2023-10-02 18:05:12 +08:00
committed by GitHub
parent 741ed4191b
commit 71cb20eb4f
68 changed files with 1353 additions and 882 deletions

View File

@@ -0,0 +1,236 @@
import { Dropdown, Input, Menu } from 'antd';
import classNames from 'classnames';
import {
useEffect,
useState,
forwardRef,
ForwardRefRenderFunction,
useImperativeHandle,
memo,
} from 'react';
import ConversationModal from '../components/ConversationModal';
import { deleteConversation, getAllConversations, saveConversation } from '../service';
import styles from './style.module.less';
import { AgentType, ConversationDetailType } from '../type';
import { DEFAULT_CONVERSATION_NAME } from '../constants';
import moment from 'moment';
import { CloseOutlined, DeleteOutlined, SearchOutlined } from '@ant-design/icons';
type Props = {
currentAgent?: AgentType;
currentConversation?: ConversationDetailType;
historyVisible?: boolean;
onSelectConversation: (
conversation: ConversationDetailType,
sendMsgParams?: any,
isAdd?: boolean
) => void;
onCloseConversation: () => void;
};
const Conversation: ForwardRefRenderFunction<any, Props> = (
{ currentAgent, currentConversation, historyVisible, onSelectConversation, onCloseConversation },
ref
) => {
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 (agentId?: number) => {
const { data } = await getAllConversations(agentId || currentAgent!.id);
const conversationList = data || [];
setConversations(conversationList.slice(0, 200));
return conversationList;
};
const initData = async () => {
const data = await updateData();
if (data.length > 0) {
onSelectConversation(data[0]);
} else {
onAddConversation();
}
};
useEffect(() => {
if (currentAgent) {
if (currentAgent.initialSendMsgParams) {
onAddConversation(currentAgent.initialSendMsgParams);
} else {
initData();
}
}
}, [currentAgent]);
const addConversation = async (sendMsgParams?: any) => {
const agentId = sendMsgParams?.agentId || currentAgent!.id;
await saveConversation(DEFAULT_CONVERSATION_NAME, agentId);
return updateData(agentId);
};
const onDeleteConversation = async (id: number) => {
await deleteConversation(id);
initData();
};
const onAddConversation = async (sendMsgParams?: any) => {
const data = await addConversation(sendMsgParams);
if (data.length > 0) {
onSelectConversation(data[0], sendMsgParams, true);
}
};
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.historyVisible]: historyVisible,
});
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.rightSection}>
<div className={styles.titleBar}>
<div className={styles.title}></div>
<div className={styles.rightOperation}>
<div
className={styles.newConversation}
onClick={() => {
addConversation();
}}
>
</div>
<CloseOutlined className={styles.closeIcon} onClick={onCloseConversation} />
</div>
</div>
<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);
}}
>
<div className={styles.conversationContent}>
<div className={styles.topTitleBar}>
<div className={styles.conversationTitleBar}>
<div className={styles.conversationName}>{item.chatName}</div>
{currentConversation?.chatId === item.chatId && (
<div className={styles.currentConversation}></div>
)}
</div>
<div className={styles.conversationTime}>
{convertTime(item.lastTime || '')}
</div>
</div>
<div className={styles.bottomSection}>
<div className={styles.subTitle}>{item.lastQuestion}</div>
<DeleteOutlined
className={styles.deleteIcon}
onClick={e => {
e.stopPropagation();
onDeleteConversation(item.chatId);
}}
/>
</div>
</div>
</div>
</Dropdown>
);
})}
</div>
</div>
<ConversationModal
visible={editModalVisible}
editConversation={editConversation}
onClose={() => {
setEditModalVisible(false);
}}
onFinish={() => {
setEditModalVisible(false);
updateData();
}}
/>
</div>
);
};
function areEqual(prevProps: Props, nextProps: Props) {
if (
prevProps.currentAgent?.id === nextProps.currentAgent?.id &&
prevProps.currentConversation?.chatId === nextProps.currentConversation?.chatId &&
prevProps.historyVisible === nextProps.historyVisible
) {
return true;
}
return false;
}
export default memo(forwardRef(Conversation), areEqual);

View File

@@ -0,0 +1,171 @@
.conversation {
position: relative;
width: 0;
height: 100%;
background: #fff;
.rightSection {
width: 100%;
height: 100%;
.titleBar {
display: flex;
align-items: center;
justify-content: space-between;
.title {
color: var(--text-color);
font-weight: 500;
font-size: 15px;
}
.rightOperation {
display: flex;
align-items: center;
column-gap: 12px;
.newConversation {
color: var(--text-color);
font-size: 14px;
cursor: pointer;
&:hover {
color: var(--chat-blue);
}
}
.closeIcon {
color: var(--text-color);
font-size: 16px;
cursor: pointer;
&:hover {
color: var(--chat-blue);
}
}
}
}
.searchConversation {
display: flex;
align-items: center;
padding: 12px 0 10px;
.searchIcon {
color: #999 !important;
}
.searchTask {
font-size: 13px;
background-color: #f5f5f5;
border: 0;
border-radius: 4px;
box-shadow: none !important;
:global {
.ant-input {
font-size: 13px !important;
background-color: transparent !important;
}
}
}
}
.conversationList {
display: flex;
flex-direction: column;
height: calc(100% - 70px);
padding: 2px 0 0;
overflow-y: auto;
row-gap: 12px;
.conversationItem {
display: flex;
align-items: center;
padding: 8px 12px;
border: 1px solid #efefef;
border-radius: 8px;
cursor: pointer;
.conversationContent {
width: 100%;
.topTitleBar {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
.conversationTitleBar {
display: flex;
align-items: center;
column-gap: 6px;
.conversationName {
max-width: 300px;
margin-right: 2px;
overflow: hidden;
color: var(--text-color);
font-weight: 500;
font-size: 14px;
white-space: nowrap;
text-overflow: ellipsis;
}
.currentConversation {
padding: 0 4px;
color: var(--chat-blue);
font-size: 12px;
background-color: var(--light-blue-background);
border-radius: 4px;
}
}
.conversationTime {
color: var(--text-color-six);
font-size: 12px;
}
}
.bottomSection {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 4px;
.subTitle {
width: 350px;
overflow: hidden;
color: var(--text-color-six);
font-size: 12px;
white-space: nowrap;
text-overflow: ellipsis;
}
.deleteIcon {
color: var(--text-color-six);
font-size: 14px;
cursor: pointer;
&:hover {
color: var(--chat-blue);
}
}
}
}
&:hover,
&.activeConversationItem {
background-color: #f0f0f0;
}
}
}
}
&.historyVisible {
width: 400px;
padding: 10px 16px;
border-left: 1px solid #f1f1f1;
z-index: 99;
}
}