mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-06-04 11:24:00 +08:00
### What problem does this PR solve? Embed the chat window into other websites through iframe #345 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
962c66714e
commit
cda7b607cb
@ -2,7 +2,11 @@ import Markdown from 'react-markdown';
|
|||||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
|
|
||||||
const SharedMarkdown = ({ content }: { content: string }) => {
|
const HightLightMarkdown = ({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: string | null | undefined;
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Markdown
|
<Markdown
|
||||||
remarkPlugins={[remarkGfm]}
|
remarkPlugins={[remarkGfm]}
|
||||||
@ -24,9 +28,9 @@ const SharedMarkdown = ({ content }: { content: string }) => {
|
|||||||
} as any
|
} as any
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{content}
|
{children}
|
||||||
</Markdown>
|
</Markdown>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SharedMarkdown;
|
export default HightLightMarkdown;
|
@ -4,7 +4,7 @@ import {
|
|||||||
IStats,
|
IStats,
|
||||||
IToken,
|
IToken,
|
||||||
} from '@/interfaces/database/chat';
|
} from '@/interfaces/database/chat';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useDispatch, useSelector } from 'umi';
|
import { useDispatch, useSelector } from 'umi';
|
||||||
|
|
||||||
export const useFetchDialogList = () => {
|
export const useFetchDialogList = () => {
|
||||||
@ -299,27 +299,4 @@ export const useCompleteSharedConversation = () => {
|
|||||||
return completeSharedConversation;
|
return completeSharedConversation;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useCreatePublicUrlToken = (dialogId: string, visible: boolean) => {
|
|
||||||
const [token, setToken] = useState();
|
|
||||||
const createToken = useCreateToken(dialogId);
|
|
||||||
const { protocol, host } = window.location;
|
|
||||||
|
|
||||||
const urlWithToken = `${protocol}//${host}/chat/share?shared_id=${token}`;
|
|
||||||
|
|
||||||
const createUrlToken = useCallback(async () => {
|
|
||||||
if (visible) {
|
|
||||||
const data = await createToken();
|
|
||||||
const urlToken = data.data?.token;
|
|
||||||
if (urlToken) {
|
|
||||||
setToken(urlToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [createToken, visible]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
createUrlToken();
|
|
||||||
}, [createUrlToken]);
|
|
||||||
|
|
||||||
return { token, createUrlToken, urlWithToken };
|
|
||||||
};
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
@ -33,3 +33,12 @@
|
|||||||
.pointerCursor() {
|
.pointerCursor() {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clearCardBody() {
|
||||||
|
:global {
|
||||||
|
.ant-card-body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -349,7 +349,7 @@ export default {
|
|||||||
'This sets the maximum length of the model’s output, measured in the number of tokens (words or pieces of words).',
|
'This sets the maximum length of the model’s output, measured in the number of tokens (words or pieces of words).',
|
||||||
quote: 'Show Quote',
|
quote: 'Show Quote',
|
||||||
quoteTip: 'Should the source of the original text be displayed?',
|
quoteTip: 'Should the source of the original text be displayed?',
|
||||||
overview: 'Overview',
|
overview: 'API',
|
||||||
pv: 'Number of messages',
|
pv: 'Number of messages',
|
||||||
uv: 'Active user number',
|
uv: 'Active user number',
|
||||||
speed: 'Token output speed',
|
speed: 'Token output speed',
|
||||||
@ -367,6 +367,14 @@ export default {
|
|||||||
createNewKey: 'Create new key',
|
createNewKey: 'Create new key',
|
||||||
created: 'Created',
|
created: 'Created',
|
||||||
action: 'Action',
|
action: 'Action',
|
||||||
|
embedModalTitle: 'Embed into website',
|
||||||
|
comingSoon: 'Coming Soon',
|
||||||
|
fullScreenTitle: 'Full Embed',
|
||||||
|
fullScreenDescription:
|
||||||
|
'Embed the following iframe into your website at the desired location',
|
||||||
|
partialTitle: 'Partial Embed',
|
||||||
|
extensionTitle: 'Chrome Extension',
|
||||||
|
tokenError: 'Please create API Token first!',
|
||||||
},
|
},
|
||||||
setting: {
|
setting: {
|
||||||
profile: 'Profile',
|
profile: 'Profile',
|
||||||
|
@ -321,7 +321,7 @@ export default {
|
|||||||
'這設置了模型輸出的最大長度,以標記(單詞或單詞片段)的數量來衡量。',
|
'這設置了模型輸出的最大長度,以標記(單詞或單詞片段)的數量來衡量。',
|
||||||
quote: '顯示引文',
|
quote: '顯示引文',
|
||||||
quoteTip: '是否應該顯示原文出處?',
|
quoteTip: '是否應該顯示原文出處?',
|
||||||
overview: '概覽',
|
overview: 'API',
|
||||||
pv: '消息數',
|
pv: '消息數',
|
||||||
uv: '活躍用戶數',
|
uv: '活躍用戶數',
|
||||||
speed: 'Token 輸出速度',
|
speed: 'Token 輸出速度',
|
||||||
@ -339,6 +339,13 @@ export default {
|
|||||||
createNewKey: '創建新密鑰',
|
createNewKey: '創建新密鑰',
|
||||||
created: '創建於',
|
created: '創建於',
|
||||||
action: '操作',
|
action: '操作',
|
||||||
|
embedModalTitle: '嵌入網站',
|
||||||
|
comingSoon: '即將推出',
|
||||||
|
fullScreenTitle: '全屏嵌入',
|
||||||
|
fullScreenDescription: '將以下iframe嵌入您的網站處於所需位置',
|
||||||
|
partialTitle: '部分嵌入',
|
||||||
|
extensionTitle: 'Chrome 插件',
|
||||||
|
tokenError: '請先創建 Api Token!',
|
||||||
},
|
},
|
||||||
setting: {
|
setting: {
|
||||||
profile: '概述',
|
profile: '概述',
|
||||||
|
@ -338,7 +338,7 @@ export default {
|
|||||||
'这设置了模型输出的最大长度,以标记(单词或单词片段)的数量来衡量。',
|
'这设置了模型输出的最大长度,以标记(单词或单词片段)的数量来衡量。',
|
||||||
quote: '显示引文',
|
quote: '显示引文',
|
||||||
quoteTip: '是否应该显示原文出处?',
|
quoteTip: '是否应该显示原文出处?',
|
||||||
overview: '概览',
|
overview: 'API',
|
||||||
pv: '消息数',
|
pv: '消息数',
|
||||||
uv: '活跃用户数',
|
uv: '活跃用户数',
|
||||||
speed: 'Token 输出速度',
|
speed: 'Token 输出速度',
|
||||||
@ -356,6 +356,13 @@ export default {
|
|||||||
createNewKey: '创建新密钥',
|
createNewKey: '创建新密钥',
|
||||||
created: '创建于',
|
created: '创建于',
|
||||||
action: '操作',
|
action: '操作',
|
||||||
|
embedModalTitle: '嵌入网站',
|
||||||
|
comingSoon: '即将推出',
|
||||||
|
fullScreenTitle: '全屏嵌入',
|
||||||
|
fullScreenDescription: '将以下iframe嵌入您的网站处于所需位置',
|
||||||
|
partialTitle: '部分嵌入',
|
||||||
|
extensionTitle: 'Chrome 插件',
|
||||||
|
tokenError: '请先创建 Api Token!',
|
||||||
},
|
},
|
||||||
setting: {
|
setting: {
|
||||||
profile: '概要',
|
profile: '概要',
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
import CopyToClipboard from '@/components/copy-to-clipboard';
|
|
||||||
import LineChart from '@/components/line-chart';
|
import LineChart from '@/components/line-chart';
|
||||||
import { useCreatePublicUrlToken } from '@/hooks/chatHooks';
|
|
||||||
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { IDialog, IStats } from '@/interfaces/database/chat';
|
import { IDialog, IStats } from '@/interfaces/database/chat';
|
||||||
import { ReloadOutlined } from '@ant-design/icons';
|
|
||||||
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';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import camelCase from 'lodash/camelCase';
|
import camelCase from 'lodash/camelCase';
|
||||||
import { Link } from 'umi';
|
|
||||||
import ChatApiKeyModal from '../chat-api-key-modal';
|
import ChatApiKeyModal from '../chat-api-key-modal';
|
||||||
import { useFetchStatsOnMount, useSelectChartStatsList } from '../hooks';
|
import EmbedModal from '../embed-modal';
|
||||||
|
import {
|
||||||
|
useFetchStatsOnMount,
|
||||||
|
usePreviewChat,
|
||||||
|
useSelectChartStatsList,
|
||||||
|
useShowEmbedModal,
|
||||||
|
} from '../hooks';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
|
|
||||||
const { Paragraph } = Typography;
|
const { Paragraph } = Typography;
|
||||||
@ -24,16 +26,18 @@ const ChatOverviewModal = ({
|
|||||||
}: IModalProps<any> & { dialog: IDialog }) => {
|
}: IModalProps<any> & { dialog: IDialog }) => {
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('chat');
|
||||||
const chartList = useSelectChartStatsList();
|
const chartList = useSelectChartStatsList();
|
||||||
const { urlWithToken, createUrlToken, token } = useCreatePublicUrlToken(
|
|
||||||
dialog.id,
|
|
||||||
visible,
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
visible: apiKeyVisible,
|
visible: apiKeyVisible,
|
||||||
hideModal: hideApiKeyModal,
|
hideModal: hideApiKeyModal,
|
||||||
showModal: showApiKeyModal,
|
showModal: showApiKeyModal,
|
||||||
} = useSetModalState();
|
} = useSetModalState();
|
||||||
|
const {
|
||||||
|
embedVisible,
|
||||||
|
hideEmbedModal,
|
||||||
|
showEmbedModal,
|
||||||
|
embedToken,
|
||||||
|
errorContextHolder,
|
||||||
|
} = useShowEmbedModal(dialog.id);
|
||||||
|
|
||||||
const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible);
|
const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible);
|
||||||
|
|
||||||
@ -41,6 +45,8 @@ const ChatOverviewModal = ({
|
|||||||
return current && current > dayjs().endOf('day');
|
return current && current > dayjs().endOf('day');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { handlePreview, contextHolder } = usePreviewChat(dialog.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal
|
<Modal
|
||||||
@ -50,36 +56,41 @@ const ChatOverviewModal = ({
|
|||||||
width={'100vw'}
|
width={'100vw'}
|
||||||
>
|
>
|
||||||
<Flex vertical gap={'middle'}>
|
<Flex vertical gap={'middle'}>
|
||||||
<Card title={dialog.name}>
|
|
||||||
<Flex gap={8} vertical>
|
|
||||||
{t('publicUrl')}
|
|
||||||
<Flex className={styles.linkText} gap={10}>
|
|
||||||
<span>{urlWithToken}</span>
|
|
||||||
<CopyToClipboard text={urlWithToken}></CopyToClipboard>
|
|
||||||
<ReloadOutlined onClick={createUrlToken} />
|
|
||||||
</Flex>
|
|
||||||
<Space size={'middle'}>
|
|
||||||
<Button>
|
|
||||||
<Link to={`/chat/share?shared_id=${token}`} target="_blank">
|
|
||||||
{t('preview')}
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
<Button>{t('embedded')}</Button>
|
|
||||||
</Space>
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
<Card title={t('backendServiceApi')}>
|
<Card title={t('backendServiceApi')}>
|
||||||
<Flex gap={8} vertical>
|
<Flex gap={8} vertical>
|
||||||
{t('serviceApiEndpoint')}
|
{t('serviceApiEndpoint')}
|
||||||
<Paragraph copyable className={styles.linkText}>
|
<Paragraph copyable className={styles.linkText}>
|
||||||
This is a copyable text.
|
https://demo.ragflow.io/v1/api/
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Space size={'middle'}>
|
<Space size={'middle'}>
|
||||||
<Button onClick={showApiKeyModal}>{t('apiKey')}</Button>
|
<Button onClick={showApiKeyModal}>{t('apiKey')}</Button>
|
||||||
|
<a
|
||||||
|
href={
|
||||||
|
'https://github.com/infiniflow/ragflow/blob/main/docs/conversation_api.md'
|
||||||
|
}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
<Button>{t('apiReference')}</Button>
|
<Button>{t('apiReference')}</Button>
|
||||||
|
</a>
|
||||||
</Space>
|
</Space>
|
||||||
</Card>
|
</Card>
|
||||||
|
<Card title={dialog.name}>
|
||||||
|
<Flex gap={8} vertical>
|
||||||
|
{t('publicUrl')}
|
||||||
|
{/* <Flex className={styles.linkText} gap={10}>
|
||||||
|
<span>{urlWithToken}</span>
|
||||||
|
<CopyToClipboard text={urlWithToken}></CopyToClipboard>
|
||||||
|
<ReloadOutlined onClick={createUrlToken} />
|
||||||
|
</Flex> */}
|
||||||
|
<Space size={'middle'}>
|
||||||
|
<Button onClick={handlePreview}>{t('preview')}</Button>
|
||||||
|
<Button onClick={showEmbedModal}>{t('embedded')}</Button>
|
||||||
|
</Space>
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Space>
|
<Space>
|
||||||
<b>{t('dateRange')}</b>
|
<b>{t('dateRange')}</b>
|
||||||
<RangePicker
|
<RangePicker
|
||||||
@ -103,6 +114,13 @@ const ChatOverviewModal = ({
|
|||||||
hideModal={hideApiKeyModal}
|
hideModal={hideApiKeyModal}
|
||||||
dialogId={dialog.id}
|
dialogId={dialog.id}
|
||||||
></ChatApiKeyModal>
|
></ChatApiKeyModal>
|
||||||
|
<EmbedModal
|
||||||
|
token={embedToken}
|
||||||
|
visible={embedVisible}
|
||||||
|
hideModal={hideEmbedModal}
|
||||||
|
></EmbedModal>
|
||||||
|
{contextHolder}
|
||||||
|
{errorContextHolder}
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
8
web/src/pages/chat/embed-modal/index.less
Normal file
8
web/src/pages/chat/embed-modal/index.less
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.codeCard {
|
||||||
|
.clearCardBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
.codeText {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #e8e8ea;
|
||||||
|
}
|
70
web/src/pages/chat/embed-modal/index.tsx
Normal file
70
web/src/pages/chat/embed-modal/index.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import CopyToClipboard from '@/components/copy-to-clipboard';
|
||||||
|
import HightLightMarkdown from '@/components/highlight-markdown';
|
||||||
|
import { useTranslate } from '@/hooks/commonHooks';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { Card, Modal, Tabs, TabsProps } from 'antd';
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
const EmbedModal = ({
|
||||||
|
visible,
|
||||||
|
hideModal,
|
||||||
|
token = '',
|
||||||
|
}: IModalProps<any> & { token: string }) => {
|
||||||
|
const { t } = useTranslate('chat');
|
||||||
|
|
||||||
|
const text = `
|
||||||
|
~~~ html
|
||||||
|
<iframe
|
||||||
|
src="https://demo.ragflow.io/chat/share?shared_id=${token}"
|
||||||
|
style="width: 100%; height: 100%; min-height: 600px"
|
||||||
|
frameborder="0"
|
||||||
|
>
|
||||||
|
</iframe>
|
||||||
|
~~~
|
||||||
|
`;
|
||||||
|
|
||||||
|
const items: TabsProps['items'] = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: t('fullScreenTitle'),
|
||||||
|
children: (
|
||||||
|
<Card
|
||||||
|
title={t('fullScreenDescription')}
|
||||||
|
extra={<CopyToClipboard text={text}></CopyToClipboard>}
|
||||||
|
className={styles.codeCard}
|
||||||
|
>
|
||||||
|
<HightLightMarkdown>{text}</HightLightMarkdown>
|
||||||
|
</Card>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: t('partialTitle'),
|
||||||
|
children: t('comingSoon'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
label: t('extensionTitle'),
|
||||||
|
children: t('comingSoon'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const onChange = (key: string) => {
|
||||||
|
console.log(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={t('embedModalTitle')}
|
||||||
|
open={visible}
|
||||||
|
style={{ top: 300 }}
|
||||||
|
width={'50vw'}
|
||||||
|
onOk={hideModal}
|
||||||
|
onCancel={hideModal}
|
||||||
|
>
|
||||||
|
<Tabs defaultActiveKey="1" items={items} onChange={onChange} />
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EmbedModal;
|
@ -14,15 +14,21 @@ import {
|
|||||||
useRemoveToken,
|
useRemoveToken,
|
||||||
useSelectConversationList,
|
useSelectConversationList,
|
||||||
useSelectDialogList,
|
useSelectDialogList,
|
||||||
|
useSelectStats,
|
||||||
useSelectTokenList,
|
useSelectTokenList,
|
||||||
useSetDialog,
|
useSetDialog,
|
||||||
useUpdateConversation,
|
useUpdateConversation,
|
||||||
} from '@/hooks/chatHooks';
|
} from '@/hooks/chatHooks';
|
||||||
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/commonHooks';
|
import {
|
||||||
|
useSetModalState,
|
||||||
|
useShowDeleteConfirm,
|
||||||
|
useTranslate,
|
||||||
|
} from '@/hooks/commonHooks';
|
||||||
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
||||||
import { IConversation, IDialog, IStats } from '@/interfaces/database/chat';
|
import { 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 dayjs, { Dayjs } from 'dayjs';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import {
|
import {
|
||||||
@ -777,35 +783,35 @@ type ChartStatsType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useSelectChartStatsList = (): ChartStatsType => {
|
export const useSelectChartStatsList = (): ChartStatsType => {
|
||||||
// const stats: IStats = useSelectStats();
|
const stats: IStats = useSelectStats();
|
||||||
const stats = {
|
// const stats = {
|
||||||
pv: [
|
// pv: [
|
||||||
['2024-06-01', 1],
|
// ['2024-06-01', 1],
|
||||||
['2024-07-24', 3],
|
// ['2024-07-24', 3],
|
||||||
['2024-09-01', 10],
|
// ['2024-09-01', 10],
|
||||||
],
|
// ],
|
||||||
uv: [
|
// uv: [
|
||||||
['2024-02-01', 0],
|
// ['2024-02-01', 0],
|
||||||
['2024-03-01', 99],
|
// ['2024-03-01', 99],
|
||||||
['2024-05-01', 3],
|
// ['2024-05-01', 3],
|
||||||
],
|
// ],
|
||||||
speed: [
|
// speed: [
|
||||||
['2024-09-01', 2],
|
// ['2024-09-01', 2],
|
||||||
['2024-09-01', 3],
|
// ['2024-09-01', 3],
|
||||||
],
|
// ],
|
||||||
tokens: [
|
// tokens: [
|
||||||
['2024-09-01', 1],
|
// ['2024-09-01', 1],
|
||||||
['2024-09-01', 3],
|
// ['2024-09-01', 3],
|
||||||
],
|
// ],
|
||||||
round: [
|
// round: [
|
||||||
['2024-09-01', 0],
|
// ['2024-09-01', 0],
|
||||||
['2024-09-01', 3],
|
// ['2024-09-01', 3],
|
||||||
],
|
// ],
|
||||||
thumb_up: [
|
// thumb_up: [
|
||||||
['2024-09-01', 3],
|
// ['2024-09-01', 3],
|
||||||
['2024-09-01', 9],
|
// ['2024-09-01', 9],
|
||||||
],
|
// ],
|
||||||
};
|
// };
|
||||||
|
|
||||||
return Object.keys(stats).reduce((pre, cur) => {
|
return Object.keys(stats).reduce((pre, cur) => {
|
||||||
const item = stats[cur as keyof IStats];
|
const item = stats[cur as keyof IStats];
|
||||||
@ -819,4 +825,93 @@ export const useSelectChartStatsList = (): ChartStatsType => {
|
|||||||
}, {} as ChartStatsType);
|
}, {} 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
|
//#endregion
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
|
import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
|
||||||
import RenameModal from '@/components/rename-modal';
|
import RenameModal from '@/components/rename-modal';
|
||||||
import { DeleteOutlined, EditOutlined, FormOutlined } from '@ant-design/icons';
|
import {
|
||||||
|
CloudOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
FormOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
@ -185,16 +190,16 @@ const Chat = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{ type: 'divider' },
|
{ type: 'divider' },
|
||||||
// {
|
{
|
||||||
// key: '3',
|
key: '3',
|
||||||
// onClick: handleShowOverviewModal(dialog),
|
onClick: handleShowOverviewModal(dialog),
|
||||||
// label: (
|
label: (
|
||||||
// <Space>
|
<Space>
|
||||||
// <ProfileOutlined />
|
<CloudOutlined />
|
||||||
// {t('overview')}
|
{t('overview')}
|
||||||
// </Space>
|
</Space>
|
||||||
// ),
|
),
|
||||||
// },
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return appItems;
|
return appItems;
|
||||||
|
@ -202,7 +202,7 @@ const model: DvaModel<ChatModelState> = {
|
|||||||
payload: data.data,
|
payload: data.data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return data.retcode;
|
return data;
|
||||||
},
|
},
|
||||||
*removeToken({ payload }, { call, put }) {
|
*removeToken({ payload }, { call, put }) {
|
||||||
const { data } = yield call(
|
const { data } = yield call(
|
||||||
|
@ -6,10 +6,10 @@ import { Avatar, Button, Flex, Input, Skeleton, Spin } from 'antd';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useSelectConversationLoading } from '../hooks';
|
import { useSelectConversationLoading } from '../hooks';
|
||||||
|
|
||||||
|
import HightLightMarkdown from '@/components/highlight-markdown';
|
||||||
import React, { ChangeEventHandler, forwardRef } from 'react';
|
import React, { ChangeEventHandler, forwardRef } from 'react';
|
||||||
import { IClientConversation } from '../interface';
|
import { IClientConversation } from '../interface';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
import SharedMarkdown from './shared-markdown';
|
|
||||||
|
|
||||||
const MessageItem = ({ item }: { item: Message }) => {
|
const MessageItem = ({ item }: { item: Message }) => {
|
||||||
const isAssistant = item.role === MessageType.Assistant;
|
const isAssistant = item.role === MessageType.Assistant;
|
||||||
@ -46,7 +46,7 @@ const MessageItem = ({ item }: { item: Message }) => {
|
|||||||
<b>{isAssistant ? '' : 'You'}</b>
|
<b>{isAssistant ? '' : 'You'}</b>
|
||||||
<div className={styles.messageText}>
|
<div className={styles.messageText}>
|
||||||
{item.content !== '' ? (
|
{item.content !== '' ? (
|
||||||
<SharedMarkdown content={item.content}></SharedMarkdown>
|
<HightLightMarkdown>{item.content}</HightLightMarkdown>
|
||||||
) : (
|
) : (
|
||||||
<Skeleton active className={styles.messageEmpty} />
|
<Skeleton active className={styles.messageEmpty} />
|
||||||
)}
|
)}
|
||||||
|
@ -98,8 +98,8 @@ request.interceptors.request.use((url: string, options: any) => {
|
|||||||
url,
|
url,
|
||||||
options: {
|
options: {
|
||||||
...options,
|
...options,
|
||||||
// data,
|
data,
|
||||||
// params,
|
params,
|
||||||
headers: {
|
headers: {
|
||||||
...(options.skipToken ? undefined : { [Authorization]: authorization }),
|
...(options.skipToken ? undefined : { [Authorization]: authorization }),
|
||||||
...options.headers,
|
...options.headers,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user