balibabu
commited on
Commit
·
7ba250b
1
Parent(s):
8f4f7c1
feat: translate EmbedModal #345 (#455)
Browse files### 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)
- web/src/{pages/chat/share/shared-markdown.tsx → components/highlight-markdown/index.tsx} +7 -3
- web/src/hooks/chatHooks.ts +1 -24
- web/src/less/mixins.less +9 -0
- web/src/locales/en.ts +9 -1
- web/src/locales/zh-traditional.ts +8 -1
- web/src/locales/zh.ts +8 -1
- web/src/pages/chat/chat-overview-modal/index.tsx +48 -30
- web/src/pages/chat/embed-modal/index.less +8 -0
- web/src/pages/chat/embed-modal/index.tsx +70 -0
- web/src/pages/chat/hooks.ts +125 -30
- web/src/pages/chat/index.tsx +16 -11
- web/src/pages/chat/model.ts +1 -1
- web/src/pages/chat/share/large.tsx +2 -2
- web/src/utils/request.ts +2 -2
web/src/{pages/chat/share/shared-markdown.tsx → components/highlight-markdown/index.tsx}
RENAMED
@@ -2,7 +2,11 @@ import Markdown from 'react-markdown';
|
|
2 |
import SyntaxHighlighter from 'react-syntax-highlighter';
|
3 |
import remarkGfm from 'remark-gfm';
|
4 |
|
5 |
-
const
|
|
|
|
|
|
|
|
|
6 |
return (
|
7 |
<Markdown
|
8 |
remarkPlugins={[remarkGfm]}
|
@@ -24,9 +28,9 @@ const SharedMarkdown = ({ content }: { content: string }) => {
|
|
24 |
} as any
|
25 |
}
|
26 |
>
|
27 |
-
{
|
28 |
</Markdown>
|
29 |
);
|
30 |
};
|
31 |
|
32 |
-
export default
|
|
|
2 |
import SyntaxHighlighter from 'react-syntax-highlighter';
|
3 |
import remarkGfm from 'remark-gfm';
|
4 |
|
5 |
+
const HightLightMarkdown = ({
|
6 |
+
children,
|
7 |
+
}: {
|
8 |
+
children: string | null | undefined;
|
9 |
+
}) => {
|
10 |
return (
|
11 |
<Markdown
|
12 |
remarkPlugins={[remarkGfm]}
|
|
|
28 |
} as any
|
29 |
}
|
30 |
>
|
31 |
+
{children}
|
32 |
</Markdown>
|
33 |
);
|
34 |
};
|
35 |
|
36 |
+
export default HightLightMarkdown;
|
web/src/hooks/chatHooks.ts
CHANGED
@@ -4,7 +4,7 @@ import {
|
|
4 |
IStats,
|
5 |
IToken,
|
6 |
} from '@/interfaces/database/chat';
|
7 |
-
import { useCallback
|
8 |
import { useDispatch, useSelector } from 'umi';
|
9 |
|
10 |
export const useFetchDialogList = () => {
|
@@ -299,27 +299,4 @@ export const useCompleteSharedConversation = () => {
|
|
299 |
return completeSharedConversation;
|
300 |
};
|
301 |
|
302 |
-
export const useCreatePublicUrlToken = (dialogId: string, visible: boolean) => {
|
303 |
-
const [token, setToken] = useState();
|
304 |
-
const createToken = useCreateToken(dialogId);
|
305 |
-
const { protocol, host } = window.location;
|
306 |
-
|
307 |
-
const urlWithToken = `${protocol}//${host}/chat/share?shared_id=${token}`;
|
308 |
-
|
309 |
-
const createUrlToken = useCallback(async () => {
|
310 |
-
if (visible) {
|
311 |
-
const data = await createToken();
|
312 |
-
const urlToken = data.data?.token;
|
313 |
-
if (urlToken) {
|
314 |
-
setToken(urlToken);
|
315 |
-
}
|
316 |
-
}
|
317 |
-
}, [createToken, visible]);
|
318 |
-
|
319 |
-
useEffect(() => {
|
320 |
-
createUrlToken();
|
321 |
-
}, [createUrlToken]);
|
322 |
-
|
323 |
-
return { token, createUrlToken, urlWithToken };
|
324 |
-
};
|
325 |
//#endregion
|
|
|
4 |
IStats,
|
5 |
IToken,
|
6 |
} from '@/interfaces/database/chat';
|
7 |
+
import { useCallback } from 'react';
|
8 |
import { useDispatch, useSelector } from 'umi';
|
9 |
|
10 |
export const useFetchDialogList = () => {
|
|
|
299 |
return completeSharedConversation;
|
300 |
};
|
301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
302 |
//#endregion
|
web/src/less/mixins.less
CHANGED
@@ -33,3 +33,12 @@
|
|
33 |
.pointerCursor() {
|
34 |
cursor: pointer;
|
35 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
.pointerCursor() {
|
34 |
cursor: pointer;
|
35 |
}
|
36 |
+
|
37 |
+
.clearCardBody() {
|
38 |
+
:global {
|
39 |
+
.ant-card-body {
|
40 |
+
padding: 0;
|
41 |
+
margin: 0;
|
42 |
+
}
|
43 |
+
}
|
44 |
+
}
|
web/src/locales/en.ts
CHANGED
@@ -349,7 +349,7 @@ export default {
|
|
349 |
'This sets the maximum length of the model’s output, measured in the number of tokens (words or pieces of words).',
|
350 |
quote: 'Show Quote',
|
351 |
quoteTip: 'Should the source of the original text be displayed?',
|
352 |
-
overview: '
|
353 |
pv: 'Number of messages',
|
354 |
uv: 'Active user number',
|
355 |
speed: 'Token output speed',
|
@@ -367,6 +367,14 @@ export default {
|
|
367 |
createNewKey: 'Create new key',
|
368 |
created: 'Created',
|
369 |
action: 'Action',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
370 |
},
|
371 |
setting: {
|
372 |
profile: 'Profile',
|
|
|
349 |
'This sets the maximum length of the model’s output, measured in the number of tokens (words or pieces of words).',
|
350 |
quote: 'Show Quote',
|
351 |
quoteTip: 'Should the source of the original text be displayed?',
|
352 |
+
overview: 'API',
|
353 |
pv: 'Number of messages',
|
354 |
uv: 'Active user number',
|
355 |
speed: 'Token output speed',
|
|
|
367 |
createNewKey: 'Create new key',
|
368 |
created: 'Created',
|
369 |
action: 'Action',
|
370 |
+
embedModalTitle: 'Embed into website',
|
371 |
+
comingSoon: 'Coming Soon',
|
372 |
+
fullScreenTitle: 'Full Embed',
|
373 |
+
fullScreenDescription:
|
374 |
+
'Embed the following iframe into your website at the desired location',
|
375 |
+
partialTitle: 'Partial Embed',
|
376 |
+
extensionTitle: 'Chrome Extension',
|
377 |
+
tokenError: 'Please create API Token first!',
|
378 |
},
|
379 |
setting: {
|
380 |
profile: 'Profile',
|
web/src/locales/zh-traditional.ts
CHANGED
@@ -321,7 +321,7 @@ export default {
|
|
321 |
'這設置了模型輸出的最大長度,以標記(單詞或單詞片段)的數量來衡量。',
|
322 |
quote: '顯示引文',
|
323 |
quoteTip: '是否應該顯示原文出處?',
|
324 |
-
overview: '
|
325 |
pv: '消息數',
|
326 |
uv: '活躍用戶數',
|
327 |
speed: 'Token 輸出速度',
|
@@ -339,6 +339,13 @@ export default {
|
|
339 |
createNewKey: '創建新密鑰',
|
340 |
created: '創建於',
|
341 |
action: '操作',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
},
|
343 |
setting: {
|
344 |
profile: '概述',
|
|
|
321 |
'這設置了模型輸出的最大長度,以標記(單詞或單詞片段)的數量來衡量。',
|
322 |
quote: '顯示引文',
|
323 |
quoteTip: '是否應該顯示原文出處?',
|
324 |
+
overview: 'API',
|
325 |
pv: '消息數',
|
326 |
uv: '活躍用戶數',
|
327 |
speed: 'Token 輸出速度',
|
|
|
339 |
createNewKey: '創建新密鑰',
|
340 |
created: '創建於',
|
341 |
action: '操作',
|
342 |
+
embedModalTitle: '嵌入網站',
|
343 |
+
comingSoon: '即將推出',
|
344 |
+
fullScreenTitle: '全屏嵌入',
|
345 |
+
fullScreenDescription: '將以下iframe嵌入您的網站處於所需位置',
|
346 |
+
partialTitle: '部分嵌入',
|
347 |
+
extensionTitle: 'Chrome 插件',
|
348 |
+
tokenError: '請先創建 Api Token!',
|
349 |
},
|
350 |
setting: {
|
351 |
profile: '概述',
|
web/src/locales/zh.ts
CHANGED
@@ -338,7 +338,7 @@ export default {
|
|
338 |
'这设置了模型输出的最大长度,以标记(单词或单词片段)的数量来衡量。',
|
339 |
quote: '显示引文',
|
340 |
quoteTip: '是否应该显示原文出处?',
|
341 |
-
overview: '
|
342 |
pv: '消息数',
|
343 |
uv: '活跃用户数',
|
344 |
speed: 'Token 输出速度',
|
@@ -356,6 +356,13 @@ export default {
|
|
356 |
createNewKey: '创建新密钥',
|
357 |
created: '创建于',
|
358 |
action: '操作',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
359 |
},
|
360 |
setting: {
|
361 |
profile: '概要',
|
|
|
338 |
'这设置了模型输出的最大长度,以标记(单词或单词片段)的数量来衡量。',
|
339 |
quote: '显示引文',
|
340 |
quoteTip: '是否应该显示原文出处?',
|
341 |
+
overview: 'API',
|
342 |
pv: '消息数',
|
343 |
uv: '活跃用户数',
|
344 |
speed: 'Token 输出速度',
|
|
|
356 |
createNewKey: '创建新密钥',
|
357 |
created: '创建于',
|
358 |
action: '操作',
|
359 |
+
embedModalTitle: '嵌入网站',
|
360 |
+
comingSoon: '即将推出',
|
361 |
+
fullScreenTitle: '全屏嵌入',
|
362 |
+
fullScreenDescription: '将以下iframe嵌入您的网站处于所需位置',
|
363 |
+
partialTitle: '部分嵌入',
|
364 |
+
extensionTitle: 'Chrome 插件',
|
365 |
+
tokenError: '请先创建 Api Token!',
|
366 |
},
|
367 |
setting: {
|
368 |
profile: '概要',
|
web/src/pages/chat/chat-overview-modal/index.tsx
CHANGED
@@ -1,17 +1,19 @@
|
|
1 |
-
import CopyToClipboard from '@/components/copy-to-clipboard';
|
2 |
import LineChart from '@/components/line-chart';
|
3 |
-
import { useCreatePublicUrlToken } from '@/hooks/chatHooks';
|
4 |
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
5 |
import { IModalProps } from '@/interfaces/common';
|
6 |
import { IDialog, IStats } from '@/interfaces/database/chat';
|
7 |
-
import { ReloadOutlined } from '@ant-design/icons';
|
8 |
import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
|
9 |
import { RangePickerProps } from 'antd/es/date-picker';
|
10 |
import dayjs from 'dayjs';
|
11 |
import camelCase from 'lodash/camelCase';
|
12 |
-
import { Link } from 'umi';
|
13 |
import ChatApiKeyModal from '../chat-api-key-modal';
|
14 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
import styles from './index.less';
|
16 |
|
17 |
const { Paragraph } = Typography;
|
@@ -24,16 +26,18 @@ const ChatOverviewModal = ({
|
|
24 |
}: IModalProps<any> & { dialog: IDialog }) => {
|
25 |
const { t } = useTranslate('chat');
|
26 |
const chartList = useSelectChartStatsList();
|
27 |
-
const { urlWithToken, createUrlToken, token } = useCreatePublicUrlToken(
|
28 |
-
dialog.id,
|
29 |
-
visible,
|
30 |
-
);
|
31 |
-
|
32 |
const {
|
33 |
visible: apiKeyVisible,
|
34 |
hideModal: hideApiKeyModal,
|
35 |
showModal: showApiKeyModal,
|
36 |
} = useSetModalState();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
|
38 |
const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible);
|
39 |
|
@@ -41,6 +45,8 @@ const ChatOverviewModal = ({
|
|
41 |
return current && current > dayjs().endOf('day');
|
42 |
};
|
43 |
|
|
|
|
|
44 |
return (
|
45 |
<>
|
46 |
<Modal
|
@@ -50,36 +56,41 @@ const ChatOverviewModal = ({
|
|
50 |
width={'100vw'}
|
51 |
>
|
52 |
<Flex vertical gap={'middle'}>
|
53 |
-
<Card title={dialog.name}>
|
54 |
-
<Flex gap={8} vertical>
|
55 |
-
{t('publicUrl')}
|
56 |
-
<Flex className={styles.linkText} gap={10}>
|
57 |
-
<span>{urlWithToken}</span>
|
58 |
-
<CopyToClipboard text={urlWithToken}></CopyToClipboard>
|
59 |
-
<ReloadOutlined onClick={createUrlToken} />
|
60 |
-
</Flex>
|
61 |
-
<Space size={'middle'}>
|
62 |
-
<Button>
|
63 |
-
<Link to={`/chat/share?shared_id=${token}`} target="_blank">
|
64 |
-
{t('preview')}
|
65 |
-
</Link>
|
66 |
-
</Button>
|
67 |
-
<Button>{t('embedded')}</Button>
|
68 |
-
</Space>
|
69 |
-
</Flex>
|
70 |
-
</Card>
|
71 |
<Card title={t('backendServiceApi')}>
|
72 |
<Flex gap={8} vertical>
|
73 |
{t('serviceApiEndpoint')}
|
74 |
<Paragraph copyable className={styles.linkText}>
|
75 |
-
|
76 |
</Paragraph>
|
77 |
</Flex>
|
78 |
<Space size={'middle'}>
|
79 |
<Button onClick={showApiKeyModal}>{t('apiKey')}</Button>
|
80 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
</Space>
|
82 |
</Card>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
<Space>
|
84 |
<b>{t('dateRange')}</b>
|
85 |
<RangePicker
|
@@ -103,6 +114,13 @@ const ChatOverviewModal = ({
|
|
103 |
hideModal={hideApiKeyModal}
|
104 |
dialogId={dialog.id}
|
105 |
></ChatApiKeyModal>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
</Modal>
|
107 |
</>
|
108 |
);
|
|
|
|
|
1 |
import LineChart from '@/components/line-chart';
|
|
|
2 |
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
|
3 |
import { IModalProps } from '@/interfaces/common';
|
4 |
import { IDialog, IStats } from '@/interfaces/database/chat';
|
|
|
5 |
import { Button, Card, DatePicker, Flex, Modal, Space, Typography } from 'antd';
|
6 |
import { RangePickerProps } from 'antd/es/date-picker';
|
7 |
import dayjs from 'dayjs';
|
8 |
import camelCase from 'lodash/camelCase';
|
|
|
9 |
import ChatApiKeyModal from '../chat-api-key-modal';
|
10 |
+
import EmbedModal from '../embed-modal';
|
11 |
+
import {
|
12 |
+
useFetchStatsOnMount,
|
13 |
+
usePreviewChat,
|
14 |
+
useSelectChartStatsList,
|
15 |
+
useShowEmbedModal,
|
16 |
+
} from '../hooks';
|
17 |
import styles from './index.less';
|
18 |
|
19 |
const { Paragraph } = Typography;
|
|
|
26 |
}: IModalProps<any> & { dialog: IDialog }) => {
|
27 |
const { t } = useTranslate('chat');
|
28 |
const chartList = useSelectChartStatsList();
|
|
|
|
|
|
|
|
|
|
|
29 |
const {
|
30 |
visible: apiKeyVisible,
|
31 |
hideModal: hideApiKeyModal,
|
32 |
showModal: showApiKeyModal,
|
33 |
} = useSetModalState();
|
34 |
+
const {
|
35 |
+
embedVisible,
|
36 |
+
hideEmbedModal,
|
37 |
+
showEmbedModal,
|
38 |
+
embedToken,
|
39 |
+
errorContextHolder,
|
40 |
+
} = useShowEmbedModal(dialog.id);
|
41 |
|
42 |
const { pickerValue, setPickerValue } = useFetchStatsOnMount(visible);
|
43 |
|
|
|
45 |
return current && current > dayjs().endOf('day');
|
46 |
};
|
47 |
|
48 |
+
const { handlePreview, contextHolder } = usePreviewChat(dialog.id);
|
49 |
+
|
50 |
return (
|
51 |
<>
|
52 |
<Modal
|
|
|
56 |
width={'100vw'}
|
57 |
>
|
58 |
<Flex vertical gap={'middle'}>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
<Card title={t('backendServiceApi')}>
|
60 |
<Flex gap={8} vertical>
|
61 |
{t('serviceApiEndpoint')}
|
62 |
<Paragraph copyable className={styles.linkText}>
|
63 |
+
https://demo.ragflow.io/v1/api/
|
64 |
</Paragraph>
|
65 |
</Flex>
|
66 |
<Space size={'middle'}>
|
67 |
<Button onClick={showApiKeyModal}>{t('apiKey')}</Button>
|
68 |
+
<a
|
69 |
+
href={
|
70 |
+
'https://github.com/infiniflow/ragflow/blob/main/docs/conversation_api.md'
|
71 |
+
}
|
72 |
+
target="_blank"
|
73 |
+
rel="noreferrer"
|
74 |
+
>
|
75 |
+
<Button>{t('apiReference')}</Button>
|
76 |
+
</a>
|
77 |
</Space>
|
78 |
</Card>
|
79 |
+
<Card title={dialog.name}>
|
80 |
+
<Flex gap={8} vertical>
|
81 |
+
{t('publicUrl')}
|
82 |
+
{/* <Flex className={styles.linkText} gap={10}>
|
83 |
+
<span>{urlWithToken}</span>
|
84 |
+
<CopyToClipboard text={urlWithToken}></CopyToClipboard>
|
85 |
+
<ReloadOutlined onClick={createUrlToken} />
|
86 |
+
</Flex> */}
|
87 |
+
<Space size={'middle'}>
|
88 |
+
<Button onClick={handlePreview}>{t('preview')}</Button>
|
89 |
+
<Button onClick={showEmbedModal}>{t('embedded')}</Button>
|
90 |
+
</Space>
|
91 |
+
</Flex>
|
92 |
+
</Card>
|
93 |
+
|
94 |
<Space>
|
95 |
<b>{t('dateRange')}</b>
|
96 |
<RangePicker
|
|
|
114 |
hideModal={hideApiKeyModal}
|
115 |
dialogId={dialog.id}
|
116 |
></ChatApiKeyModal>
|
117 |
+
<EmbedModal
|
118 |
+
token={embedToken}
|
119 |
+
visible={embedVisible}
|
120 |
+
hideModal={hideEmbedModal}
|
121 |
+
></EmbedModal>
|
122 |
+
{contextHolder}
|
123 |
+
{errorContextHolder}
|
124 |
</Modal>
|
125 |
</>
|
126 |
);
|
web/src/pages/chat/embed-modal/index.less
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.codeCard {
|
2 |
+
.clearCardBody();
|
3 |
+
}
|
4 |
+
|
5 |
+
.codeText {
|
6 |
+
padding: 10px;
|
7 |
+
background-color: #e8e8ea;
|
8 |
+
}
|
web/src/pages/chat/embed-modal/index.tsx
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import CopyToClipboard from '@/components/copy-to-clipboard';
|
2 |
+
import HightLightMarkdown from '@/components/highlight-markdown';
|
3 |
+
import { useTranslate } from '@/hooks/commonHooks';
|
4 |
+
import { IModalProps } from '@/interfaces/common';
|
5 |
+
import { Card, Modal, Tabs, TabsProps } from 'antd';
|
6 |
+
import styles from './index.less';
|
7 |
+
|
8 |
+
const EmbedModal = ({
|
9 |
+
visible,
|
10 |
+
hideModal,
|
11 |
+
token = '',
|
12 |
+
}: IModalProps<any> & { token: string }) => {
|
13 |
+
const { t } = useTranslate('chat');
|
14 |
+
|
15 |
+
const text = `
|
16 |
+
~~~ html
|
17 |
+
<iframe
|
18 |
+
src="https://demo.ragflow.io/chat/share?shared_id=${token}"
|
19 |
+
style="width: 100%; height: 100%; min-height: 600px"
|
20 |
+
frameborder="0"
|
21 |
+
>
|
22 |
+
</iframe>
|
23 |
+
~~~
|
24 |
+
`;
|
25 |
+
|
26 |
+
const items: TabsProps['items'] = [
|
27 |
+
{
|
28 |
+
key: '1',
|
29 |
+
label: t('fullScreenTitle'),
|
30 |
+
children: (
|
31 |
+
<Card
|
32 |
+
title={t('fullScreenDescription')}
|
33 |
+
extra={<CopyToClipboard text={text}></CopyToClipboard>}
|
34 |
+
className={styles.codeCard}
|
35 |
+
>
|
36 |
+
<HightLightMarkdown>{text}</HightLightMarkdown>
|
37 |
+
</Card>
|
38 |
+
),
|
39 |
+
},
|
40 |
+
{
|
41 |
+
key: '2',
|
42 |
+
label: t('partialTitle'),
|
43 |
+
children: t('comingSoon'),
|
44 |
+
},
|
45 |
+
{
|
46 |
+
key: '3',
|
47 |
+
label: t('extensionTitle'),
|
48 |
+
children: t('comingSoon'),
|
49 |
+
},
|
50 |
+
];
|
51 |
+
|
52 |
+
const onChange = (key: string) => {
|
53 |
+
console.log(key);
|
54 |
+
};
|
55 |
+
|
56 |
+
return (
|
57 |
+
<Modal
|
58 |
+
title={t('embedModalTitle')}
|
59 |
+
open={visible}
|
60 |
+
style={{ top: 300 }}
|
61 |
+
width={'50vw'}
|
62 |
+
onOk={hideModal}
|
63 |
+
onCancel={hideModal}
|
64 |
+
>
|
65 |
+
<Tabs defaultActiveKey="1" items={items} onChange={onChange} />
|
66 |
+
</Modal>
|
67 |
+
);
|
68 |
+
};
|
69 |
+
|
70 |
+
export default EmbedModal;
|
web/src/pages/chat/hooks.ts
CHANGED
@@ -14,15 +14,21 @@ import {
|
|
14 |
useRemoveToken,
|
15 |
useSelectConversationList,
|
16 |
useSelectDialogList,
|
|
|
17 |
useSelectTokenList,
|
18 |
useSetDialog,
|
19 |
useUpdateConversation,
|
20 |
} from '@/hooks/chatHooks';
|
21 |
-
import {
|
|
|
|
|
|
|
|
|
22 |
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
23 |
import { IConversation, IDialog, IStats } from '@/interfaces/database/chat';
|
24 |
import { IChunk } from '@/interfaces/database/knowledge';
|
25 |
import { getFileExtension } from '@/utils';
|
|
|
26 |
import dayjs, { Dayjs } from 'dayjs';
|
27 |
import omit from 'lodash/omit';
|
28 |
import {
|
@@ -777,35 +783,35 @@ type ChartStatsType = {
|
|
777 |
};
|
778 |
|
779 |
export const useSelectChartStatsList = (): ChartStatsType => {
|
780 |
-
|
781 |
-
const stats = {
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
};
|
809 |
|
810 |
return Object.keys(stats).reduce((pre, cur) => {
|
811 |
const item = stats[cur as keyof IStats];
|
@@ -819,4 +825,93 @@ export const useSelectChartStatsList = (): ChartStatsType => {
|
|
819 |
}, {} as ChartStatsType);
|
820 |
};
|
821 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
822 |
//#endregion
|
|
|
14 |
useRemoveToken,
|
15 |
useSelectConversationList,
|
16 |
useSelectDialogList,
|
17 |
+
useSelectStats,
|
18 |
useSelectTokenList,
|
19 |
useSetDialog,
|
20 |
useUpdateConversation,
|
21 |
} from '@/hooks/chatHooks';
|
22 |
+
import {
|
23 |
+
useSetModalState,
|
24 |
+
useShowDeleteConfirm,
|
25 |
+
useTranslate,
|
26 |
+
} from '@/hooks/commonHooks';
|
27 |
import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
|
28 |
import { IConversation, IDialog, IStats } from '@/interfaces/database/chat';
|
29 |
import { IChunk } from '@/interfaces/database/knowledge';
|
30 |
import { getFileExtension } from '@/utils';
|
31 |
+
import { message } from 'antd';
|
32 |
import dayjs, { Dayjs } from 'dayjs';
|
33 |
import omit from 'lodash/omit';
|
34 |
import {
|
|
|
783 |
};
|
784 |
|
785 |
export const useSelectChartStatsList = (): ChartStatsType => {
|
786 |
+
const stats: IStats = useSelectStats();
|
787 |
+
// const stats = {
|
788 |
+
// pv: [
|
789 |
+
// ['2024-06-01', 1],
|
790 |
+
// ['2024-07-24', 3],
|
791 |
+
// ['2024-09-01', 10],
|
792 |
+
// ],
|
793 |
+
// uv: [
|
794 |
+
// ['2024-02-01', 0],
|
795 |
+
// ['2024-03-01', 99],
|
796 |
+
// ['2024-05-01', 3],
|
797 |
+
// ],
|
798 |
+
// speed: [
|
799 |
+
// ['2024-09-01', 2],
|
800 |
+
// ['2024-09-01', 3],
|
801 |
+
// ],
|
802 |
+
// tokens: [
|
803 |
+
// ['2024-09-01', 1],
|
804 |
+
// ['2024-09-01', 3],
|
805 |
+
// ],
|
806 |
+
// round: [
|
807 |
+
// ['2024-09-01', 0],
|
808 |
+
// ['2024-09-01', 3],
|
809 |
+
// ],
|
810 |
+
// thumb_up: [
|
811 |
+
// ['2024-09-01', 3],
|
812 |
+
// ['2024-09-01', 9],
|
813 |
+
// ],
|
814 |
+
// };
|
815 |
|
816 |
return Object.keys(stats).reduce((pre, cur) => {
|
817 |
const item = stats[cur as keyof IStats];
|
|
|
825 |
}, {} as ChartStatsType);
|
826 |
};
|
827 |
|
828 |
+
export const useShowTokenEmptyError = () => {
|
829 |
+
const [messageApi, contextHolder] = message.useMessage();
|
830 |
+
const { t } = useTranslate('chat');
|
831 |
+
|
832 |
+
const showTokenEmptyError = useCallback(() => {
|
833 |
+
messageApi.error(t('tokenError'));
|
834 |
+
}, [messageApi, t]);
|
835 |
+
return { showTokenEmptyError, contextHolder };
|
836 |
+
};
|
837 |
+
|
838 |
+
const getUrlWithToken = (token: string) => {
|
839 |
+
const { protocol, host } = window.location;
|
840 |
+
return `${protocol}//${host}/chat/share?shared_id=${token}`;
|
841 |
+
};
|
842 |
+
|
843 |
+
const useFetchTokenListBeforeOtherStep = (dialogId: string) => {
|
844 |
+
const { showTokenEmptyError, contextHolder } = useShowTokenEmptyError();
|
845 |
+
|
846 |
+
const listToken = useListToken();
|
847 |
+
const tokenList = useSelectTokenList();
|
848 |
+
|
849 |
+
const token =
|
850 |
+
Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
|
851 |
+
|
852 |
+
const handleOperate = useCallback(async () => {
|
853 |
+
const data = await listToken(dialogId);
|
854 |
+
const list = data.data;
|
855 |
+
if (data.retcode === 0 && Array.isArray(list) && list.length > 0) {
|
856 |
+
return list[0]?.token;
|
857 |
+
} else {
|
858 |
+
showTokenEmptyError();
|
859 |
+
return false;
|
860 |
+
}
|
861 |
+
}, [dialogId, listToken, showTokenEmptyError]);
|
862 |
+
|
863 |
+
return {
|
864 |
+
token,
|
865 |
+
contextHolder,
|
866 |
+
handleOperate,
|
867 |
+
};
|
868 |
+
};
|
869 |
+
|
870 |
+
export const useShowEmbedModal = (dialogId: string) => {
|
871 |
+
const {
|
872 |
+
visible: embedVisible,
|
873 |
+
hideModal: hideEmbedModal,
|
874 |
+
showModal: showEmbedModal,
|
875 |
+
} = useSetModalState();
|
876 |
+
|
877 |
+
const { handleOperate, token, contextHolder } =
|
878 |
+
useFetchTokenListBeforeOtherStep(dialogId);
|
879 |
+
|
880 |
+
const handleShowEmbedModal = useCallback(async () => {
|
881 |
+
const succeed = await handleOperate();
|
882 |
+
if (succeed) {
|
883 |
+
showEmbedModal();
|
884 |
+
}
|
885 |
+
}, [handleOperate, showEmbedModal]);
|
886 |
+
|
887 |
+
return {
|
888 |
+
showEmbedModal: handleShowEmbedModal,
|
889 |
+
hideEmbedModal,
|
890 |
+
embedVisible,
|
891 |
+
embedToken: token,
|
892 |
+
errorContextHolder: contextHolder,
|
893 |
+
};
|
894 |
+
};
|
895 |
+
|
896 |
+
export const usePreviewChat = (dialogId: string) => {
|
897 |
+
const { handleOperate, contextHolder } =
|
898 |
+
useFetchTokenListBeforeOtherStep(dialogId);
|
899 |
+
|
900 |
+
const open = useCallback((t: string) => {
|
901 |
+
window.open(getUrlWithToken(t), '_blank');
|
902 |
+
}, []);
|
903 |
+
|
904 |
+
const handlePreview = useCallback(async () => {
|
905 |
+
const token = await handleOperate();
|
906 |
+
if (token) {
|
907 |
+
open(token);
|
908 |
+
}
|
909 |
+
}, [handleOperate, open]);
|
910 |
+
|
911 |
+
return {
|
912 |
+
handlePreview,
|
913 |
+
contextHolder,
|
914 |
+
};
|
915 |
+
};
|
916 |
+
|
917 |
//#endregion
|
web/src/pages/chat/index.tsx
CHANGED
@@ -1,6 +1,11 @@
|
|
1 |
import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
|
2 |
import RenameModal from '@/components/rename-modal';
|
3 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
4 |
import {
|
5 |
Avatar,
|
6 |
Button,
|
@@ -185,16 +190,16 @@ const Chat = () => {
|
|
185 |
),
|
186 |
},
|
187 |
{ type: 'divider' },
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
];
|
199 |
|
200 |
return appItems;
|
|
|
1 |
import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg';
|
2 |
import RenameModal from '@/components/rename-modal';
|
3 |
+
import {
|
4 |
+
CloudOutlined,
|
5 |
+
DeleteOutlined,
|
6 |
+
EditOutlined,
|
7 |
+
FormOutlined,
|
8 |
+
} from '@ant-design/icons';
|
9 |
import {
|
10 |
Avatar,
|
11 |
Button,
|
|
|
190 |
),
|
191 |
},
|
192 |
{ type: 'divider' },
|
193 |
+
{
|
194 |
+
key: '3',
|
195 |
+
onClick: handleShowOverviewModal(dialog),
|
196 |
+
label: (
|
197 |
+
<Space>
|
198 |
+
<CloudOutlined />
|
199 |
+
{t('overview')}
|
200 |
+
</Space>
|
201 |
+
),
|
202 |
+
},
|
203 |
];
|
204 |
|
205 |
return appItems;
|
web/src/pages/chat/model.ts
CHANGED
@@ -202,7 +202,7 @@ const model: DvaModel<ChatModelState> = {
|
|
202 |
payload: data.data,
|
203 |
});
|
204 |
}
|
205 |
-
return data
|
206 |
},
|
207 |
*removeToken({ payload }, { call, put }) {
|
208 |
const { data } = yield call(
|
|
|
202 |
payload: data.data,
|
203 |
});
|
204 |
}
|
205 |
+
return data;
|
206 |
},
|
207 |
*removeToken({ payload }, { call, put }) {
|
208 |
const { data } = yield call(
|
web/src/pages/chat/share/large.tsx
CHANGED
@@ -6,10 +6,10 @@ import { Avatar, Button, Flex, Input, Skeleton, Spin } from 'antd';
|
|
6 |
import classNames from 'classnames';
|
7 |
import { useSelectConversationLoading } from '../hooks';
|
8 |
|
|
|
9 |
import React, { ChangeEventHandler, forwardRef } from 'react';
|
10 |
import { IClientConversation } from '../interface';
|
11 |
import styles from './index.less';
|
12 |
-
import SharedMarkdown from './shared-markdown';
|
13 |
|
14 |
const MessageItem = ({ item }: { item: Message }) => {
|
15 |
const isAssistant = item.role === MessageType.Assistant;
|
@@ -46,7 +46,7 @@ const MessageItem = ({ item }: { item: Message }) => {
|
|
46 |
<b>{isAssistant ? '' : 'You'}</b>
|
47 |
<div className={styles.messageText}>
|
48 |
{item.content !== '' ? (
|
49 |
-
<
|
50 |
) : (
|
51 |
<Skeleton active className={styles.messageEmpty} />
|
52 |
)}
|
|
|
6 |
import classNames from 'classnames';
|
7 |
import { useSelectConversationLoading } from '../hooks';
|
8 |
|
9 |
+
import HightLightMarkdown from '@/components/highlight-markdown';
|
10 |
import React, { ChangeEventHandler, forwardRef } from 'react';
|
11 |
import { IClientConversation } from '../interface';
|
12 |
import styles from './index.less';
|
|
|
13 |
|
14 |
const MessageItem = ({ item }: { item: Message }) => {
|
15 |
const isAssistant = item.role === MessageType.Assistant;
|
|
|
46 |
<b>{isAssistant ? '' : 'You'}</b>
|
47 |
<div className={styles.messageText}>
|
48 |
{item.content !== '' ? (
|
49 |
+
<HightLightMarkdown>{item.content}</HightLightMarkdown>
|
50 |
) : (
|
51 |
<Skeleton active className={styles.messageEmpty} />
|
52 |
)}
|
web/src/utils/request.ts
CHANGED
@@ -98,8 +98,8 @@ request.interceptors.request.use((url: string, options: any) => {
|
|
98 |
url,
|
99 |
options: {
|
100 |
...options,
|
101 |
-
|
102 |
-
|
103 |
headers: {
|
104 |
...(options.skipToken ? undefined : { [Authorization]: authorization }),
|
105 |
...options.headers,
|
|
|
98 |
url,
|
99 |
options: {
|
100 |
...options,
|
101 |
+
data,
|
102 |
+
params,
|
103 |
headers: {
|
104 |
...(options.skipToken ? undefined : { [Authorization]: authorization }),
|
105 |
...options.headers,
|