feat: Expose the agent's chat window to third parties #1842 (#1897)

### What problem does this PR solve?

feat: Expose the agent's chat window to third parties #1842
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-08-09 18:59:16 +08:00 committed by GitHub
parent 37be0ff3d3
commit 827042f72b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 289 additions and 357 deletions

View File

@ -9,12 +9,12 @@ import { Button, Modal, Space, Table } from 'antd';
import { useOperateApiKey } from '../hooks'; import { useOperateApiKey } from '../hooks';
const ChatApiKeyModal = ({ const ChatApiKeyModal = ({
visible,
dialogId, dialogId,
hideModal, hideModal,
}: IModalProps<any> & { dialogId: string }) => { idKey,
}: IModalProps<any> & { dialogId: string; idKey: string }) => {
const { createToken, removeToken, tokenList, listLoading, creatingLoading } = const { createToken, removeToken, tokenList, listLoading, creatingLoading } =
useOperateApiKey(visible, dialogId); useOperateApiKey(dialogId, idKey);
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
const columns: TableProps<IToken>['columns'] = [ const columns: TableProps<IToken>['columns'] = [
@ -48,7 +48,7 @@ const ChatApiKeyModal = ({
<> <>
<Modal <Modal
title={t('apiKey')} title={t('apiKey')}
open={visible} open
onCancel={hideModal} onCancel={hideModal}
cancelButtonProps={{ style: { display: 'none' } }} cancelButtonProps={{ style: { display: 'none' } }}
style={{ top: 300 }} style={{ top: 300 }}

View File

@ -1,7 +1,8 @@
import LineChart from '@/components/line-chart'; import LineChart from '@/components/line-chart';
import { useFetchNextStats } from '@/hooks/chat-hooks';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IDialog, IStats } from '@/interfaces/database/chat'; import { IStats } from '@/interfaces/database/chat';
import { formatDate } from '@/utils/date'; import { formatDate } from '@/utils/date';
import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd'; import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
import { RangePickerProps } from 'antd/es/date-picker'; import { RangePickerProps } from 'antd/es/date-picker';
@ -10,7 +11,6 @@ import camelCase from 'lodash/camelCase';
import ChatApiKeyModal from '../chat-api-key-modal'; import ChatApiKeyModal from '../chat-api-key-modal';
import EmbedModal from '../embed-modal'; import EmbedModal from '../embed-modal';
import { import {
useFetchStatsOnMount,
usePreviewChat, usePreviewChat,
useSelectChartStatsList, useSelectChartStatsList,
useShowEmbedModal, useShowEmbedModal,
@ -40,8 +40,10 @@ const StatsLineChart = ({ statsType }: { statsType: keyof IStats }) => {
const ChatOverviewModal = ({ const ChatOverviewModal = ({
visible, visible,
hideModal, hideModal,
dialog, id,
}: IModalProps<any> & { dialog: IDialog }) => { name = '',
idKey,
}: IModalProps<any> & { id: string; name?: string; idKey: string }) => {
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
const { const {
visible: apiKeyVisible, visible: apiKeyVisible,
@ -54,15 +56,15 @@ const ChatOverviewModal = ({
showEmbedModal, showEmbedModal,
embedToken, embedToken,
errorContextHolder, errorContextHolder,
} = useShowEmbedModal(dialog.id); } = useShowEmbedModal(id, idKey);
const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible); const { pickerValue, setPickerValue } = useFetchNextStats();
const disabledDate: RangePickerProps['disabledDate'] = (current) => { const disabledDate: RangePickerProps['disabledDate'] = (current) => {
return current && current > dayjs().endOf('day'); return current && current > dayjs().endOf('day');
}; };
const { handlePreview, contextHolder } = usePreviewChat(dialog.id); const { handlePreview, contextHolder } = usePreviewChat(id, idKey);
return ( return (
<> <>
@ -97,7 +99,7 @@ const ChatOverviewModal = ({
</a> </a>
</Space> </Space>
</Card> </Card>
<Card title={`${dialog.name} Web App`}> <Card title={`${name} Web App`}>
<Flex gap={8} vertical> <Flex gap={8} vertical>
<Space size={'middle'}> <Space size={'middle'}>
<Button onClick={handlePreview}>{t('preview')}</Button> <Button onClick={handlePreview}>{t('preview')}</Button>
@ -124,11 +126,13 @@ const ChatOverviewModal = ({
<StatsLineChart statsType={'uv'}></StatsLineChart> <StatsLineChart statsType={'uv'}></StatsLineChart>
</div> </div>
</Flex> </Flex>
<ChatApiKeyModal {apiKeyVisible && (
visible={apiKeyVisible} <ChatApiKeyModal
hideModal={hideApiKeyModal} hideModal={hideApiKeyModal}
dialogId={dialog.id} dialogId={id}
></ChatApiKeyModal> idKey={idKey}
></ChatApiKeyModal>
)}
<EmbedModal <EmbedModal
token={embedToken} token={embedToken}
visible={embedVisible} visible={embedVisible}

View File

@ -0,0 +1,151 @@
import {
useCreateNextToken,
useFetchNextStats,
useFetchTokenList,
useRemoveNextToken,
} from '@/hooks/chat-hooks';
import {
useSetModalState,
useShowDeleteConfirm,
useTranslate,
} from '@/hooks/common-hooks';
import { IStats } from '@/interfaces/database/chat';
import { message } from 'antd';
import { useCallback } from 'react';
export const useOperateApiKey = (dialogId: string, idKey: string) => {
const { removeToken } = useRemoveNextToken();
const { createToken, loading: creatingLoading } = useCreateNextToken();
const { data: tokenList, loading: listLoading } = useFetchTokenList({
[idKey]: dialogId,
});
const showDeleteConfirm = useShowDeleteConfirm();
const onRemoveToken = (token: string, tenantId: string) => {
showDeleteConfirm({
onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
});
};
const onCreateToken = useCallback(() => {
createToken({ [idKey]: dialogId });
}, [createToken, idKey, dialogId]);
return {
removeToken: onRemoveToken,
createToken: onCreateToken,
tokenList,
creatingLoading,
listLoading,
};
};
type ChartStatsType = {
[k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>;
};
export const useSelectChartStatsList = (): ChartStatsType => {
const { data: stats } = useFetchNextStats();
return Object.keys(stats).reduce((pre, cur) => {
const item = stats[cur as keyof IStats];
if (item.length > 0) {
pre[cur as keyof IStats] = item.map((x) => ({
xAxis: x[0] as string,
yAxis: x[1] as number,
}));
}
return pre;
}, {} as ChartStatsType);
};
export const useShowTokenEmptyError = () => {
const [messageApi, contextHolder] = message.useMessage();
const { t } = useTranslate('chat');
const showTokenEmptyError = useCallback(() => {
messageApi.error(t('tokenError'));
}, [messageApi, t]);
return { showTokenEmptyError, contextHolder };
};
const getUrlWithToken = (token: string) => {
const { protocol, host } = window.location;
return `${protocol}//${host}/chat/share?shared_id=${token}`;
};
const useFetchTokenListBeforeOtherStep = (dialogId: string, idKey: string) => {
const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();
const { data: tokenList, refetch } = useFetchTokenList({ [idKey]: dialogId });
const token =
Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
const handleOperate = useCallback(async () => {
const ret = await refetch();
const list = ret.data;
if (Array.isArray(list) && list.length > 0) {
return list[0]?.token;
} else {
showTokenEmptyError();
return false;
}
}, [showTokenEmptyError, refetch]);
return {
token,
contextHolder,
handleOperate,
};
};
export const useShowEmbedModal = (dialogId: string, idKey: string) => {
const {
visible: embedVisible,
hideModal: hideEmbedModal,
showModal: showEmbedModal,
} = useSetModalState();
const { handleOperate, token, contextHolder } =
useFetchTokenListBeforeOtherStep(dialogId, idKey);
const handleShowEmbedModal = useCallback(async () => {
const succeed = await handleOperate();
if (succeed) {
showEmbedModal();
}
}, [handleOperate, showEmbedModal]);
return {
showEmbedModal: handleShowEmbedModal,
hideEmbedModal,
embedVisible,
embedToken: token,
errorContextHolder: contextHolder,
};
};
export const usePreviewChat = (dialogId: string, idKey: string) => {
const { handleOperate, contextHolder } = useFetchTokenListBeforeOtherStep(
dialogId,
idKey,
);
const open = useCallback((t: string) => {
window.open(getUrlWithToken(t), '_blank');
}, []);
const handlePreview = useCallback(async () => {
const token = await handleOperate();
if (token) {
open(token);
}
}, [handleOperate, open]);
return {
handlePreview,
contextHolder,
};
};

View File

@ -4,7 +4,10 @@ import {
IStats, IStats,
IToken, IToken,
} from '@/interfaces/database/chat'; } from '@/interfaces/database/chat';
import { useCallback } from 'react'; import chatService from '@/services/chat-service';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import dayjs, { Dayjs } from 'dayjs';
import { useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'umi'; import { useDispatch, useSelector } from 'umi';
export const useFetchDialogList = () => { export const useFetchDialogList = () => {
@ -175,79 +178,108 @@ export const useCompleteConversation = () => {
// #region API provided for external calls // #region API provided for external calls
export const useCreateToken = (dialogId: string) => { export const useCreateToken = (params: Record<string, any>) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const createToken = useCallback(() => { const createToken = useCallback(() => {
return dispatch<any>({ return dispatch<any>({
type: 'chatModel/createToken', type: 'chatModel/createToken',
payload: { dialogId }, payload: params,
}); });
}, [dispatch, dialogId]); }, [dispatch, params]);
return createToken; return createToken;
}; };
export const useListToken = () => { export const useCreateNextToken = () => {
const dispatch = useDispatch(); const queryClient = useQueryClient();
const {
const listToken = useCallback( data,
(dialogId: string) => { isPending: loading,
return dispatch<any>({ mutateAsync,
type: 'chatModel/listToken', } = useMutation({
payload: { dialogId }, mutationKey: ['createToken'],
}); mutationFn: async (params: Record<string, any>) => {
const { data } = await chatService.createToken(params);
if (data.retcode === 0) {
queryClient.invalidateQueries({ queryKey: ['fetchTokenList'] });
}
return data?.data ?? [];
}, },
[dispatch], });
);
return listToken; return { data, loading, createToken: mutateAsync };
}; };
export const useSelectTokenList = () => { export const useFetchTokenList = (params: Record<string, any>) => {
const tokenList: IToken[] = useSelector( const {
(state: any) => state.chatModel.tokenList, data,
); isFetching: loading,
refetch,
} = useQuery<IToken[]>({
queryKey: ['fetchTokenList', params],
initialData: [],
gcTime: 0,
queryFn: async () => {
const { data } = await chatService.listToken(params);
return tokenList; return data?.data ?? [];
};
export const useRemoveToken = () => {
const dispatch = useDispatch();
const removeToken = useCallback(
(payload: { tenantId: string; dialogId: string; tokens: string[] }) => {
return dispatch<any>({
type: 'chatModel/removeToken',
payload: payload,
});
}, },
[dispatch], });
);
return removeToken; return { data, loading, refetch };
}; };
export const useFetchStats = () => { export const useRemoveNextToken = () => {
const dispatch = useDispatch(); const queryClient = useQueryClient();
const {
const fetchStats = useCallback( data,
(payload: any) => { isPending: loading,
return dispatch<any>({ mutateAsync,
type: 'chatModel/getStats', } = useMutation({
payload, mutationKey: ['removeToken'],
}); mutationFn: async (params: {
tenantId: string;
dialogId: string;
tokens: string[];
}) => {
const { data } = await chatService.removeToken(params);
if (data.retcode === 0) {
queryClient.invalidateQueries({ queryKey: ['fetchTokenList'] });
}
return data?.data ?? [];
}, },
[dispatch], });
);
return fetchStats; return { data, loading, removeToken: mutateAsync };
}; };
export const useSelectStats = () => { type RangeValue = [Dayjs | null, Dayjs | null] | null;
const stats: IStats = useSelector((state: any) => state.chatModel.stats);
return stats; const getDay = (date?: Dayjs) => date?.format('YYYY-MM-DD');
export const useFetchNextStats = () => {
const [pickerValue, setPickerValue] = useState<RangeValue>([
dayjs(),
dayjs().subtract(7, 'day'),
]);
const { data, isFetching: loading } = useQuery<IStats>({
queryKey: ['fetchStats', pickerValue],
initialData: {} as IStats,
gcTime: 0,
queryFn: async () => {
if (Array.isArray(pickerValue) && pickerValue[0]) {
const { data } = await chatService.getStats({
fromDate: getDay(pickerValue[0]),
toDate: getDay(pickerValue[1] ?? dayjs()),
});
return data?.data ?? {};
}
return {};
},
});
return { data, loading, pickerValue, setPickerValue };
}; };
//#endregion //#endregion

View File

@ -783,6 +783,7 @@ The above is the content you need to summarize.`,
'15d': '12 days', '15d': '12 days',
'30d': '30 days', '30d': '30 days',
}, },
publish: 'Publish',
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',

View File

@ -741,6 +741,7 @@ export default {
'15d': '12天', '15d': '12天',
'30d': '30天', '30d': '30天',
}, },
publish: '發布',
}, },
footer: { footer: {
profile: '“保留所有權利 @ react”', profile: '“保留所有權利 @ react”',

View File

@ -759,6 +759,7 @@ export default {
'15d': '12天', '15d': '12天',
'30d': '30天', '30d': '30天',
}, },
publish: '发布',
}, },
footer: { footer: {
profile: 'All rights reserved @ React', profile: 'All rights reserved @ React',

View File

@ -1,20 +1,14 @@
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { fileIconMap } from '@/constants/common'; import { fileIconMap } from '@/constants/common';
import { import {
useCreateToken,
useFetchConversation, useFetchConversation,
useFetchConversationList, useFetchConversationList,
useFetchDialog, useFetchDialog,
useFetchDialogList, useFetchDialogList,
useFetchStats,
useListToken,
useRemoveConversation, useRemoveConversation,
useRemoveDialog, useRemoveDialog,
useRemoveToken,
useSelectConversationList, useSelectConversationList,
useSelectDialogList, useSelectDialogList,
useSelectStats,
useSelectTokenList,
useSetDialog, useSetDialog,
useUpdateConversation, useUpdateConversation,
} from '@/hooks/chat-hooks'; } from '@/hooks/chat-hooks';
@ -25,16 +19,9 @@ import {
} from '@/hooks/common-hooks'; } from '@/hooks/common-hooks';
import { useSendMessageWithSse } from '@/hooks/logic-hooks'; import { useSendMessageWithSse } from '@/hooks/logic-hooks';
import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks'; import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks';
import { import { IAnswer, IConversation, IDialog } from '@/interfaces/database/chat';
IAnswer,
IConversation,
IDialog,
IStats,
} from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import { getFileExtension } from '@/utils'; import { getFileExtension } from '@/utils';
import { message } from 'antd';
import dayjs, { Dayjs } from 'dayjs';
import omit from 'lodash/omit'; import omit from 'lodash/omit';
import trim from 'lodash/trim'; import trim from 'lodash/trim';
import { import {
@ -773,202 +760,3 @@ export const useSendButtonDisabled = (value: string) => {
return trim(value) === ''; return trim(value) === '';
}; };
//#endregion //#endregion
//#region API provided for external calls
type RangeValue = [Dayjs | null, Dayjs | null] | null;
const getDay = (date: Dayjs) => date.format('YYYY-MM-DD');
export const useFetchStatsOnMount = (visible: boolean) => {
const fetchStats = useFetchStats();
const [pickerValue, setPickerValue] = useState<RangeValue>([
dayjs(),
dayjs().subtract(7, 'day'),
]);
useEffect(() => {
if (visible && Array.isArray(pickerValue) && pickerValue[0]) {
fetchStats({
fromDate: getDay(pickerValue[0]),
toDate: getDay(pickerValue[1] ?? dayjs()),
});
}
}, [fetchStats, pickerValue, visible]);
return {
pickerValue,
setPickerValue,
};
};
export const useOperateApiKey = (visible: boolean, dialogId: string) => {
const removeToken = useRemoveToken();
const createToken = useCreateToken(dialogId);
const listToken = useListToken();
const tokenList = useSelectTokenList();
const creatingLoading = useOneNamespaceEffectsLoading('chatModel', [
'createToken',
]);
const listLoading = useOneNamespaceEffectsLoading('chatModel', ['list']);
const showDeleteConfirm = useShowDeleteConfirm();
const onRemoveToken = (token: string, tenantId: string) => {
showDeleteConfirm({
onOk: () => removeToken({ dialogId, tokens: [token], tenantId }),
});
};
useEffect(() => {
if (visible && dialogId) {
listToken(dialogId);
}
}, [listToken, dialogId, visible]);
return {
removeToken: onRemoveToken,
createToken,
tokenList,
creatingLoading,
listLoading,
};
};
type ChartStatsType = {
[k in keyof IStats]: Array<{ xAxis: string; yAxis: number }>;
};
export const useSelectChartStatsList = (): ChartStatsType => {
const stats: IStats = useSelectStats();
// const stats = {
// pv: [
// ['2024-06-01', 1],
// ['2024-07-24', 3],
// ['2024-09-01', 10],
// ],
// uv: [
// ['2024-02-01', 0],
// ['2024-03-01', 99],
// ['2024-05-01', 3],
// ],
// speed: [
// ['2024-09-01', 2],
// ['2024-09-01', 3],
// ],
// tokens: [
// ['2024-09-01', 1],
// ['2024-09-01', 3],
// ],
// round: [
// ['2024-09-01', 0],
// ['2024-09-01', 3],
// ],
// thumb_up: [
// ['2024-09-01', 3],
// ['2024-09-01', 9],
// ],
// };
return Object.keys(stats).reduce((pre, cur) => {
const item = stats[cur as keyof IStats];
if (item.length > 0) {
pre[cur as keyof IStats] = item.map((x) => ({
xAxis: x[0] as string,
yAxis: x[1] as number,
}));
}
return pre;
}, {} as ChartStatsType);
};
export const useShowTokenEmptyError = () => {
const [messageApi, contextHolder] = message.useMessage();
const { t } = useTranslate('chat');
const showTokenEmptyError = useCallback(() => {
messageApi.error(t('tokenError'));
}, [messageApi, t]);
return { showTokenEmptyError, contextHolder };
};
const getUrlWithToken = (token: string) => {
const { protocol, host } = window.location;
return `${protocol}//${host}/chat/share?shared_id=${token}`;
};
const useFetchTokenListBeforeOtherStep = (dialogId: string) => {
const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();
const listToken = useListToken();
const tokenList = useSelectTokenList();
const token =
Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
const handleOperate = useCallback(async () => {
const data = await listToken(dialogId);
const list = data.data;
if (data.retcode === 0 && Array.isArray(list) && list.length > 0) {
return list[0]?.token;
} else {
showTokenEmptyError();
return false;
}
}, [dialogId, listToken, showTokenEmptyError]);
return {
token,
contextHolder,
handleOperate,
};
};
export const useShowEmbedModal = (dialogId: string) => {
const {
visible: embedVisible,
hideModal: hideEmbedModal,
showModal: showEmbedModal,
} = useSetModalState();
const { handleOperate, token, contextHolder } =
useFetchTokenListBeforeOtherStep(dialogId);
const handleShowEmbedModal = useCallback(async () => {
const succeed = await handleOperate();
if (succeed) {
showEmbedModal();
}
}, [handleOperate, showEmbedModal]);
return {
showEmbedModal: handleShowEmbedModal,
hideEmbedModal,
embedVisible,
embedToken: token,
errorContextHolder: contextHolder,
};
};
export const usePreviewChat = (dialogId: string) => {
const { handleOperate, contextHolder } =
useFetchTokenListBeforeOtherStep(dialogId);
const open = useCallback((t: string) => {
window.open(getUrlWithToken(t), '_blank');
}, []);
const handlePreview = useCallback(async () => {
const token = await handleOperate();
if (token) {
open(token);
}
}, [handleOperate, open]);
return {
handlePreview,
contextHolder,
};
};
//#endregion

View File

@ -41,10 +41,10 @@ import {
useSelectFirstDialogOnMount, useSelectFirstDialogOnMount,
} from './hooks'; } from './hooks';
import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { useSetSelectedRecord } from '@/hooks/logic-hooks'; import { useSetSelectedRecord } from '@/hooks/logic-hooks';
import { IDialog } from '@/interfaces/database/chat'; import { IDialog } from '@/interfaces/database/chat';
import ChatOverviewModal from './chat-overview-modal';
import styles from './index.less'; import styles from './index.less';
const { Text } = Typography; const { Text } = Typography;
@ -371,11 +371,15 @@ const Chat = () => {
initialName={initialConversationName} initialName={initialConversationName}
loading={conversationRenameLoading} loading={conversationRenameLoading}
></RenameModal> ></RenameModal>
<ChatOverviewModal {overviewVisible && (
visible={overviewVisible} <ChatOverviewModal
hideModal={hideOverviewModal} visible={overviewVisible}
dialog={currentRecord} hideModal={hideOverviewModal}
></ChatOverviewModal> id={currentRecord.id}
name={currentRecord.name}
idKey="dialogId"
></ChatOverviewModal>
)}
</Flex> </Flex>
); );
}; };

View File

@ -1,14 +1,7 @@
import { import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
IConversation,
IDialog,
IStats,
IToken,
Message,
} from '@/interfaces/database/chat';
import i18n from '@/locales/config'; import i18n from '@/locales/config';
import chatService from '@/services/chat-service'; import chatService from '@/services/chat-service';
import { message } from 'antd'; import { message } from 'antd';
import omit from 'lodash/omit';
import { DvaModel } from 'umi'; import { DvaModel } from 'umi';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { IClientConversation, IMessage } from './interface'; import { IClientConversation, IMessage } from './interface';
@ -20,8 +13,6 @@ export interface ChatModelState {
currentDialog: IDialog; currentDialog: IDialog;
conversationList: IConversation[]; conversationList: IConversation[];
currentConversation: IClientConversation; currentConversation: IClientConversation;
tokenList: IToken[];
stats: IStats;
} }
const model: DvaModel<ChatModelState> = { const model: DvaModel<ChatModelState> = {
@ -32,8 +23,6 @@ const model: DvaModel<ChatModelState> = {
currentDialog: <IDialog>{}, currentDialog: <IDialog>{},
conversationList: [], conversationList: [],
currentConversation: {} as IClientConversation, currentConversation: {} as IClientConversation,
tokenList: [],
stats: {} as IStats,
}, },
reducers: { reducers: {
save(state, action) { save(state, action) {
@ -71,18 +60,6 @@ const model: DvaModel<ChatModelState> = {
currentConversation: { ...payload, message: messageList }, currentConversation: { ...payload, message: messageList },
}; };
}, },
setTokenList(state, { payload }) {
return {
...state,
tokenList: payload,
};
},
setStats(state, { payload }) {
return {
...state,
stats: payload,
};
},
}, },
effects: { effects: {
@ -183,51 +160,6 @@ const model: DvaModel<ChatModelState> = {
} }
return data.retcode; return data.retcode;
}, },
*createToken({ payload }, { call, put }) {
const { data } = yield call(chatService.createToken, payload);
if (data.retcode === 0) {
yield put({
type: 'listToken',
payload: payload,
});
message.success(i18n.t('message.created'));
}
return data;
},
*listToken({ payload }, { call, put }) {
const { data } = yield call(chatService.listToken, payload);
if (data.retcode === 0) {
yield put({
type: 'setTokenList',
payload: data.data,
});
}
return data;
},
*removeToken({ payload }, { call, put }) {
const { data } = yield call(
chatService.removeToken,
omit(payload, ['dialogId']),
);
if (data.retcode === 0) {
message.success(i18n.t('message.deleted'));
yield put({
type: 'listToken',
payload: { dialog_id: payload.dialogId },
});
}
return data.retcode;
},
*getStats({ payload }, { call, put }) {
const { data } = yield call(chatService.getStats, payload);
if (data.retcode === 0) {
yield put({
type: 'setStats',
payload: data.data,
});
}
return data.retcode;
},
*createExternalConversation({ payload }, { call, put }) { *createExternalConversation({ payload }, { call, put }) {
const { data } = yield call( const { data } = yield call(
chatService.createExternalConversation, chatService.createExternalConversation,

View File

@ -1,8 +1,9 @@
import { useTranslate } from '@/hooks/common-hooks'; import ChatOverviewModal from '@/components/api-service/chat-overview-modal';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { useFetchFlow } from '@/hooks/flow-hooks'; import { useFetchFlow } from '@/hooks/flow-hooks';
import { ArrowLeftOutlined } from '@ant-design/icons'; import { ArrowLeftOutlined } from '@ant-design/icons';
import { Button, Flex, Space } from 'antd'; import { Button, Flex, Space } from 'antd';
import { Link } from 'umi'; import { Link, useParams } from 'umi';
import { useSaveGraph, useSaveGraphBeforeOpeningDebugDrawer } from '../hooks'; import { useSaveGraph, useSaveGraphBeforeOpeningDebugDrawer } from '../hooks';
import styles from './index.less'; import styles from './index.less';
@ -15,6 +16,12 @@ const FlowHeader = ({ showChatDrawer }: IProps) => {
const handleRun = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer); const handleRun = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
const { data } = useFetchFlow(); const { data } = useFetchFlow();
const { t } = useTranslate('flow'); const { t } = useTranslate('flow');
const {
visible: overviewVisible,
hideModal: hideOverviewModal,
showModal: showOverviewModal,
} = useSetModalState();
const { id } = useParams();
return ( return (
<> <>
@ -37,8 +44,19 @@ const FlowHeader = ({ showChatDrawer }: IProps) => {
<Button type="primary" onClick={saveGraph}> <Button type="primary" onClick={saveGraph}>
<b>{t('save')}</b> <b>{t('save')}</b>
</Button> </Button>
<Button type="primary" onClick={showOverviewModal}>
<b>{t('publish')}</b>
</Button>
</Space> </Space>
</Flex> </Flex>
{overviewVisible && (
<ChatOverviewModal
visible={overviewVisible}
hideModal={hideOverviewModal}
id={id!}
idKey="canvasId"
></ChatOverviewModal>
)}
</> </>
); );
}; };