### 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)tags/v0.10.0
| @@ -9,12 +9,12 @@ import { Button, Modal, Space, Table } from 'antd'; | |||
| import { useOperateApiKey } from '../hooks'; | |||
| const ChatApiKeyModal = ({ | |||
| visible, | |||
| dialogId, | |||
| hideModal, | |||
| }: IModalProps<any> & { dialogId: string }) => { | |||
| idKey, | |||
| }: IModalProps<any> & { dialogId: string; idKey: string }) => { | |||
| const { createToken, removeToken, tokenList, listLoading, creatingLoading } = | |||
| useOperateApiKey(visible, dialogId); | |||
| useOperateApiKey(dialogId, idKey); | |||
| const { t } = useTranslate('chat'); | |||
| const columns: TableProps<IToken>['columns'] = [ | |||
| @@ -48,7 +48,7 @@ const ChatApiKeyModal = ({ | |||
| <> | |||
| <Modal | |||
| title={t('apiKey')} | |||
| open={visible} | |||
| open | |||
| onCancel={hideModal} | |||
| cancelButtonProps={{ style: { display: 'none' } }} | |||
| style={{ top: 300 }} | |||
| @@ -1,7 +1,8 @@ | |||
| import LineChart from '@/components/line-chart'; | |||
| import { useFetchNextStats } from '@/hooks/chat-hooks'; | |||
| import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; | |||
| import { IModalProps } from '@/interfaces/common'; | |||
| import { IDialog, IStats } from '@/interfaces/database/chat'; | |||
| import { IStats } from '@/interfaces/database/chat'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd'; | |||
| import { RangePickerProps } from 'antd/es/date-picker'; | |||
| @@ -10,7 +11,6 @@ import camelCase from 'lodash/camelCase'; | |||
| import ChatApiKeyModal from '../chat-api-key-modal'; | |||
| import EmbedModal from '../embed-modal'; | |||
| import { | |||
| useFetchStatsOnMount, | |||
| usePreviewChat, | |||
| useSelectChartStatsList, | |||
| useShowEmbedModal, | |||
| @@ -40,8 +40,10 @@ const StatsLineChart = ({ statsType }: { statsType: keyof IStats }) => { | |||
| const ChatOverviewModal = ({ | |||
| visible, | |||
| hideModal, | |||
| dialog, | |||
| }: IModalProps<any> & { dialog: IDialog }) => { | |||
| id, | |||
| name = '', | |||
| idKey, | |||
| }: IModalProps<any> & { id: string; name?: string; idKey: string }) => { | |||
| const { t } = useTranslate('chat'); | |||
| const { | |||
| visible: apiKeyVisible, | |||
| @@ -54,15 +56,15 @@ const ChatOverviewModal = ({ | |||
| showEmbedModal, | |||
| embedToken, | |||
| errorContextHolder, | |||
| } = useShowEmbedModal(dialog.id); | |||
| } = useShowEmbedModal(id, idKey); | |||
| const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible); | |||
| const { pickerValue, setPickerValue } = useFetchNextStats(); | |||
| const disabledDate: RangePickerProps['disabledDate'] = (current) => { | |||
| return current && current > dayjs().endOf('day'); | |||
| }; | |||
| const { handlePreview, contextHolder } = usePreviewChat(dialog.id); | |||
| const { handlePreview, contextHolder } = usePreviewChat(id, idKey); | |||
| return ( | |||
| <> | |||
| @@ -97,7 +99,7 @@ const ChatOverviewModal = ({ | |||
| </a> | |||
| </Space> | |||
| </Card> | |||
| <Card title={`${dialog.name} Web App`}> | |||
| <Card title={`${name} Web App`}> | |||
| <Flex gap={8} vertical> | |||
| <Space size={'middle'}> | |||
| <Button onClick={handlePreview}>{t('preview')}</Button> | |||
| @@ -124,11 +126,13 @@ const ChatOverviewModal = ({ | |||
| <StatsLineChart statsType={'uv'}></StatsLineChart> | |||
| </div> | |||
| </Flex> | |||
| <ChatApiKeyModal | |||
| visible={apiKeyVisible} | |||
| hideModal={hideApiKeyModal} | |||
| dialogId={dialog.id} | |||
| ></ChatApiKeyModal> | |||
| {apiKeyVisible && ( | |||
| <ChatApiKeyModal | |||
| hideModal={hideApiKeyModal} | |||
| dialogId={id} | |||
| idKey={idKey} | |||
| ></ChatApiKeyModal> | |||
| )} | |||
| <EmbedModal | |||
| token={embedToken} | |||
| visible={embedVisible} | |||
| @@ -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, | |||
| }; | |||
| }; | |||
| @@ -4,7 +4,10 @@ import { | |||
| IStats, | |||
| IToken, | |||
| } 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'; | |||
| export const useFetchDialogList = () => { | |||
| @@ -175,79 +178,108 @@ export const useCompleteConversation = () => { | |||
| // #region API provided for external calls | |||
| export const useCreateToken = (dialogId: string) => { | |||
| export const useCreateToken = (params: Record<string, any>) => { | |||
| const dispatch = useDispatch(); | |||
| const createToken = useCallback(() => { | |||
| return dispatch<any>({ | |||
| type: 'chatModel/createToken', | |||
| payload: { dialogId }, | |||
| payload: params, | |||
| }); | |||
| }, [dispatch, dialogId]); | |||
| }, [dispatch, params]); | |||
| return createToken; | |||
| }; | |||
| export const useListToken = () => { | |||
| const dispatch = useDispatch(); | |||
| const listToken = useCallback( | |||
| (dialogId: string) => { | |||
| return dispatch<any>({ | |||
| type: 'chatModel/listToken', | |||
| payload: { dialogId }, | |||
| }); | |||
| export const useCreateNextToken = () => { | |||
| const queryClient = useQueryClient(); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| 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 = () => { | |||
| const tokenList: IToken[] = useSelector( | |||
| (state: any) => state.chatModel.tokenList, | |||
| ); | |||
| return tokenList; | |||
| }; | |||
| export const useRemoveToken = () => { | |||
| const dispatch = useDispatch(); | |||
| const removeToken = useCallback( | |||
| (payload: { tenantId: string; dialogId: string; tokens: string[] }) => { | |||
| return dispatch<any>({ | |||
| type: 'chatModel/removeToken', | |||
| payload: payload, | |||
| }); | |||
| export const useFetchTokenList = (params: Record<string, any>) => { | |||
| const { | |||
| data, | |||
| isFetching: loading, | |||
| refetch, | |||
| } = useQuery<IToken[]>({ | |||
| queryKey: ['fetchTokenList', params], | |||
| initialData: [], | |||
| gcTime: 0, | |||
| queryFn: async () => { | |||
| const { data } = await chatService.listToken(params); | |||
| return data?.data ?? []; | |||
| }, | |||
| [dispatch], | |||
| ); | |||
| }); | |||
| return removeToken; | |||
| return { data, loading, refetch }; | |||
| }; | |||
| export const useFetchStats = () => { | |||
| const dispatch = useDispatch(); | |||
| const fetchStats = useCallback( | |||
| (payload: any) => { | |||
| return dispatch<any>({ | |||
| type: 'chatModel/getStats', | |||
| payload, | |||
| }); | |||
| export const useRemoveNextToken = () => { | |||
| const queryClient = useQueryClient(); | |||
| const { | |||
| data, | |||
| isPending: loading, | |||
| mutateAsync, | |||
| } = useMutation({ | |||
| 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 = () => { | |||
| const stats: IStats = useSelector((state: any) => state.chatModel.stats); | |||
| type RangeValue = [Dayjs | null, Dayjs | null] | null; | |||
| 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 stats; | |||
| return { data, loading, pickerValue, setPickerValue }; | |||
| }; | |||
| //#endregion | |||
| @@ -783,6 +783,7 @@ The above is the content you need to summarize.`, | |||
| '15d': '12 days', | |||
| '30d': '30 days', | |||
| }, | |||
| publish: 'Publish', | |||
| }, | |||
| footer: { | |||
| profile: 'All rights reserved @ React', | |||
| @@ -741,6 +741,7 @@ export default { | |||
| '15d': '12天', | |||
| '30d': '30天', | |||
| }, | |||
| publish: '發布', | |||
| }, | |||
| footer: { | |||
| profile: '“保留所有權利 @ react”', | |||
| @@ -759,6 +759,7 @@ export default { | |||
| '15d': '12天', | |||
| '30d': '30天', | |||
| }, | |||
| publish: '发布', | |||
| }, | |||
| footer: { | |||
| profile: 'All rights reserved @ React', | |||
| @@ -1,20 +1,14 @@ | |||
| import { MessageType } from '@/constants/chat'; | |||
| import { fileIconMap } from '@/constants/common'; | |||
| import { | |||
| useCreateToken, | |||
| useFetchConversation, | |||
| useFetchConversationList, | |||
| useFetchDialog, | |||
| useFetchDialogList, | |||
| useFetchStats, | |||
| useListToken, | |||
| useRemoveConversation, | |||
| useRemoveDialog, | |||
| useRemoveToken, | |||
| useSelectConversationList, | |||
| useSelectDialogList, | |||
| useSelectStats, | |||
| useSelectTokenList, | |||
| useSetDialog, | |||
| useUpdateConversation, | |||
| } from '@/hooks/chat-hooks'; | |||
| @@ -25,16 +19,9 @@ import { | |||
| } from '@/hooks/common-hooks'; | |||
| import { useSendMessageWithSse } from '@/hooks/logic-hooks'; | |||
| import { useOneNamespaceEffectsLoading } from '@/hooks/store-hooks'; | |||
| import { | |||
| IAnswer, | |||
| IConversation, | |||
| IDialog, | |||
| IStats, | |||
| } from '@/interfaces/database/chat'; | |||
| import { IAnswer, IConversation, IDialog } from '@/interfaces/database/chat'; | |||
| import { IChunk } from '@/interfaces/database/knowledge'; | |||
| import { getFileExtension } from '@/utils'; | |||
| import { message } from 'antd'; | |||
| import dayjs, { Dayjs } from 'dayjs'; | |||
| import omit from 'lodash/omit'; | |||
| import trim from 'lodash/trim'; | |||
| import { | |||
| @@ -773,202 +760,3 @@ export const useSendButtonDisabled = (value: string) => { | |||
| return trim(value) === ''; | |||
| }; | |||
| //#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 | |||
| @@ -41,10 +41,10 @@ import { | |||
| useSelectFirstDialogOnMount, | |||
| } from './hooks'; | |||
| import ChatOverviewModal from '@/components/api-service/chat-overview-modal'; | |||
| import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; | |||
| import { useSetSelectedRecord } from '@/hooks/logic-hooks'; | |||
| import { IDialog } from '@/interfaces/database/chat'; | |||
| import ChatOverviewModal from './chat-overview-modal'; | |||
| import styles from './index.less'; | |||
| const { Text } = Typography; | |||
| @@ -371,11 +371,15 @@ const Chat = () => { | |||
| initialName={initialConversationName} | |||
| loading={conversationRenameLoading} | |||
| ></RenameModal> | |||
| <ChatOverviewModal | |||
| visible={overviewVisible} | |||
| hideModal={hideOverviewModal} | |||
| dialog={currentRecord} | |||
| ></ChatOverviewModal> | |||
| {overviewVisible && ( | |||
| <ChatOverviewModal | |||
| visible={overviewVisible} | |||
| hideModal={hideOverviewModal} | |||
| id={currentRecord.id} | |||
| name={currentRecord.name} | |||
| idKey="dialogId" | |||
| ></ChatOverviewModal> | |||
| )} | |||
| </Flex> | |||
| ); | |||
| }; | |||
| @@ -1,14 +1,7 @@ | |||
| import { | |||
| IConversation, | |||
| IDialog, | |||
| IStats, | |||
| IToken, | |||
| Message, | |||
| } from '@/interfaces/database/chat'; | |||
| import { IConversation, IDialog, Message } from '@/interfaces/database/chat'; | |||
| import i18n from '@/locales/config'; | |||
| import chatService from '@/services/chat-service'; | |||
| import { message } from 'antd'; | |||
| import omit from 'lodash/omit'; | |||
| import { DvaModel } from 'umi'; | |||
| import { v4 as uuid } from 'uuid'; | |||
| import { IClientConversation, IMessage } from './interface'; | |||
| @@ -20,8 +13,6 @@ export interface ChatModelState { | |||
| currentDialog: IDialog; | |||
| conversationList: IConversation[]; | |||
| currentConversation: IClientConversation; | |||
| tokenList: IToken[]; | |||
| stats: IStats; | |||
| } | |||
| const model: DvaModel<ChatModelState> = { | |||
| @@ -32,8 +23,6 @@ const model: DvaModel<ChatModelState> = { | |||
| currentDialog: <IDialog>{}, | |||
| conversationList: [], | |||
| currentConversation: {} as IClientConversation, | |||
| tokenList: [], | |||
| stats: {} as IStats, | |||
| }, | |||
| reducers: { | |||
| save(state, action) { | |||
| @@ -71,18 +60,6 @@ const model: DvaModel<ChatModelState> = { | |||
| currentConversation: { ...payload, message: messageList }, | |||
| }; | |||
| }, | |||
| setTokenList(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| tokenList: payload, | |||
| }; | |||
| }, | |||
| setStats(state, { payload }) { | |||
| return { | |||
| ...state, | |||
| stats: payload, | |||
| }; | |||
| }, | |||
| }, | |||
| effects: { | |||
| @@ -183,51 +160,6 @@ const model: DvaModel<ChatModelState> = { | |||
| } | |||
| 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 }) { | |||
| const { data } = yield call( | |||
| chatService.createExternalConversation, | |||
| @@ -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 { ArrowLeftOutlined } from '@ant-design/icons'; | |||
| import { Button, Flex, Space } from 'antd'; | |||
| import { Link } from 'umi'; | |||
| import { Link, useParams } from 'umi'; | |||
| import { useSaveGraph, useSaveGraphBeforeOpeningDebugDrawer } from '../hooks'; | |||
| import styles from './index.less'; | |||
| @@ -15,6 +16,12 @@ const FlowHeader = ({ showChatDrawer }: IProps) => { | |||
| const handleRun = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer); | |||
| const { data } = useFetchFlow(); | |||
| const { t } = useTranslate('flow'); | |||
| const { | |||
| visible: overviewVisible, | |||
| hideModal: hideOverviewModal, | |||
| showModal: showOverviewModal, | |||
| } = useSetModalState(); | |||
| const { id } = useParams(); | |||
| return ( | |||
| <> | |||
| @@ -37,8 +44,19 @@ const FlowHeader = ({ showChatDrawer }: IProps) => { | |||
| <Button type="primary" onClick={saveGraph}> | |||
| <b>{t('save')}</b> | |||
| </Button> | |||
| <Button type="primary" onClick={showOverviewModal}> | |||
| <b>{t('publish')}</b> | |||
| </Button> | |||
| </Space> | |||
| </Flex> | |||
| {overviewVisible && ( | |||
| <ChatOverviewModal | |||
| visible={overviewVisible} | |||
| hideModal={hideOverviewModal} | |||
| id={id!} | |||
| idKey="canvasId" | |||
| ></ChatOverviewModal> | |||
| )} | |||
| </> | |||
| ); | |||
| }; | |||