balibabu commited on
Commit
700dff9
·
1 Parent(s): af9205c

fix: replace some pictures of chunk method #437 (#438)

Browse files

### What problem does this PR solve?

some chunk method pictures are not in English #437

feat: set the height of both html and body to 100%
feat: add SharedChat
feat: add shared hooks

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

web/src/assets/svg/chunk-method/law-02.svg CHANGED
web/src/assets/svg/chunk-method/manual-02.svg CHANGED
web/src/assets/svg/chunk-method/manual-04.svg CHANGED
web/src/assets/svg/chunk-method/qa-01.svg CHANGED
web/src/assets/svg/chunk-method/qa-02.svg CHANGED
web/src/assets/svg/chunk-method/resume-02.svg CHANGED
web/src/assets/svg/chunk-method/table-01.svg CHANGED
web/src/assets/svg/chunk-method/table-02.svg CHANGED
web/src/global.less CHANGED
@@ -1,6 +1,19 @@
1
  @import url(./inter.less);
2
 
 
 
 
 
3
  body {
4
  font-family: Inter;
5
  margin: 0;
 
 
 
 
 
 
 
 
 
6
  }
 
1
  @import url(./inter.less);
2
 
3
+ html {
4
+ height: 100%;
5
+ }
6
+
7
  body {
8
  font-family: Inter;
9
  margin: 0;
10
+ height: 100%;
11
+ }
12
+
13
+ #root {
14
+ height: 100%;
15
+ }
16
+
17
+ .ant-app {
18
+ height: 100%;
19
  }
web/src/hooks/chatHooks.ts CHANGED
@@ -4,7 +4,7 @@ import {
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 = () => {
@@ -248,3 +248,78 @@ export const useSelectStats = () => {
248
  };
249
 
250
  //#endregion
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  IStats,
5
  IToken,
6
  } from '@/interfaces/database/chat';
7
+ import { useCallback, useEffect, useState } from 'react';
8
  import { useDispatch, useSelector } from 'umi';
9
 
10
  export const useFetchDialogList = () => {
 
248
  };
249
 
250
  //#endregion
251
+
252
+ //#region shared chat
253
+
254
+ export const useCreateSharedConversation = () => {
255
+ const dispatch = useDispatch();
256
+
257
+ const createSharedConversation = useCallback(
258
+ (userId?: string) => {
259
+ return dispatch<any>({
260
+ type: 'chatModel/createExternalConversation',
261
+ payload: { userId },
262
+ });
263
+ },
264
+ [dispatch],
265
+ );
266
+
267
+ return createSharedConversation;
268
+ };
269
+
270
+ export const useFetchSharedConversation = () => {
271
+ const dispatch = useDispatch();
272
+
273
+ const fetchSharedConversation = useCallback(
274
+ (conversationId: string) => {
275
+ return dispatch<any>({
276
+ type: 'chatModel/getExternalConversation',
277
+ payload: conversationId,
278
+ });
279
+ },
280
+ [dispatch],
281
+ );
282
+
283
+ return fetchSharedConversation;
284
+ };
285
+
286
+ export const useCompleteSharedConversation = () => {
287
+ const dispatch = useDispatch();
288
+
289
+ const completeSharedConversation = useCallback(
290
+ (payload: any) => {
291
+ return dispatch<any>({
292
+ type: 'chatModel/completeExternalConversation',
293
+ payload: payload,
294
+ });
295
+ },
296
+ [dispatch],
297
+ );
298
+
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
web/src/pages/chat/chat-container/index.less CHANGED
@@ -33,9 +33,9 @@
33
  .messageEmpty {
34
  width: 300px;
35
  }
36
- .referenceIcon {
37
- padding: 0 6px;
38
- }
39
  }
40
 
41
  .messageItemLeft {
@@ -46,24 +46,24 @@
46
  text-align: right;
47
  }
48
 
49
- .referencePopoverWrapper {
50
- max-width: 50vw;
51
- }
52
 
53
- .referenceChunkImage {
54
- width: 10vw;
55
- object-fit: contain;
56
- }
57
 
58
- .referenceImagePreview {
59
- max-width: 45vw;
60
- max-height: 45vh;
61
- }
62
- .chunkContentText {
63
- .chunkText;
64
- max-height: 45vh;
65
- overflow-y: auto;
66
- }
67
- .documentLink {
68
- padding: 0;
69
- }
 
33
  .messageEmpty {
34
  width: 300px;
35
  }
36
+ // .referenceIcon {
37
+ // padding: 0 6px;
38
+ // }
39
  }
40
 
41
  .messageItemLeft {
 
46
  text-align: right;
47
  }
48
 
49
+ // .referencePopoverWrapper {
50
+ // max-width: 50vw;
51
+ // }
52
 
53
+ // .referenceChunkImage {
54
+ // width: 10vw;
55
+ // object-fit: contain;
56
+ // }
57
 
58
+ // .referenceImagePreview {
59
+ // max-width: 45vw;
60
+ // max-height: 45vh;
61
+ // }
62
+ // .chunkContentText {
63
+ // .chunkText;
64
+ // max-height: 45vh;
65
+ // overflow-y: auto;
66
+ // }
67
+ // .documentLink {
68
+ // padding: 0;
69
+ // }
web/src/pages/chat/chat-container/index.tsx CHANGED
@@ -1,5 +1,4 @@
1
  import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
2
- import Image from '@/components/image';
3
  import NewDocumentLink from '@/components/new-document-link';
4
  import DocumentPreviewer from '@/components/pdf-previewer';
5
  import { MessageType } from '@/constants/chat';
@@ -7,7 +6,6 @@ import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
7
  import { useSelectUserInfo } from '@/hooks/userSettingHook';
8
  import { IReference, Message } from '@/interfaces/database/chat';
9
  import { IChunk } from '@/interfaces/database/knowledge';
10
- import { InfoCircleOutlined } from '@ant-design/icons';
11
  import {
12
  Avatar,
13
  Button,
@@ -15,18 +13,11 @@ import {
15
  Flex,
16
  Input,
17
  List,
18
- Popover,
19
  Skeleton,
20
- Space,
21
  Spin,
22
  } from 'antd';
23
  import classNames from 'classnames';
24
- import { useCallback, useMemo } from 'react';
25
- import Markdown from 'react-markdown';
26
- import reactStringReplace from 'react-string-replace';
27
- import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
28
- import remarkGfm from 'remark-gfm';
29
- import { visitParents } from 'unist-util-visit-parents';
30
  import {
31
  useClickDrawer,
32
  useFetchConversationOnMount,
@@ -35,33 +26,13 @@ import {
35
  useSelectConversationLoading,
36
  useSendMessage,
37
  } from '../hooks';
 
38
 
39
  import SvgIcon from '@/components/svg-icon';
40
  import { useTranslate } from '@/hooks/commonHooks';
41
  import { getExtension, isPdf } from '@/utils/documentUtils';
42
  import styles from './index.less';
43
 
44
- const reg = /(#{2}\d+\${2})/g;
45
-
46
- const getChunkIndex = (match: string) => Number(match.slice(2, -2));
47
-
48
- const rehypeWrapReference = () => {
49
- return function wrapTextTransform(tree: any) {
50
- visitParents(tree, 'text', (node, ancestors) => {
51
- const latestAncestor = ancestors.at(-1);
52
- if (
53
- latestAncestor.tagName !== 'custom-typography' &&
54
- latestAncestor.tagName !== 'code'
55
- ) {
56
- node.type = 'element';
57
- node.tagName = 'custom-typography';
58
- node.properties = {};
59
- node.children = [{ type: 'text', value: node.value }];
60
- }
61
- });
62
- };
63
- };
64
-
65
  const MessageItem = ({
66
  item,
67
  reference,
@@ -76,100 +47,6 @@ const MessageItem = ({
76
 
77
  const isAssistant = item.role === MessageType.Assistant;
78
 
79
- const handleDocumentButtonClick = useCallback(
80
- (documentId: string, chunk: IChunk, isPdf: boolean) => () => {
81
- if (!isPdf) {
82
- return;
83
- }
84
- clickDocumentButton(documentId, chunk);
85
- },
86
- [clickDocumentButton],
87
- );
88
-
89
- const getPopoverContent = useCallback(
90
- (chunkIndex: number) => {
91
- const chunks = reference?.chunks ?? [];
92
- const chunkItem = chunks[chunkIndex];
93
- const document = reference?.doc_aggs.find(
94
- (x) => x?.doc_id === chunkItem?.doc_id,
95
- );
96
- const documentId = document?.doc_id;
97
- const fileThumbnail = documentId ? fileThumbnails[documentId] : '';
98
- const fileExtension = documentId ? getExtension(document?.doc_name) : '';
99
- const imageId = chunkItem?.img_id;
100
- return (
101
- <Flex
102
- key={chunkItem?.chunk_id}
103
- gap={10}
104
- className={styles.referencePopoverWrapper}
105
- >
106
- {imageId && (
107
- <Popover
108
- placement="left"
109
- content={
110
- <Image
111
- id={imageId}
112
- className={styles.referenceImagePreview}
113
- ></Image>
114
- }
115
- >
116
- <Image
117
- id={imageId}
118
- className={styles.referenceChunkImage}
119
- ></Image>
120
- </Popover>
121
- )}
122
- <Space direction={'vertical'}>
123
- <div
124
- dangerouslySetInnerHTML={{
125
- __html: chunkItem?.content_with_weight,
126
- }}
127
- className={styles.chunkContentText}
128
- ></div>
129
- {documentId && (
130
- <Flex gap={'small'}>
131
- {fileThumbnail ? (
132
- <img src={fileThumbnail} alt="" />
133
- ) : (
134
- <SvgIcon
135
- name={`file-icon/${fileExtension}`}
136
- width={24}
137
- ></SvgIcon>
138
- )}
139
- <Button
140
- type="link"
141
- className={styles.documentLink}
142
- onClick={handleDocumentButtonClick(
143
- documentId,
144
- chunkItem,
145
- fileExtension === 'pdf',
146
- )}
147
- >
148
- {document?.doc_name}
149
- </Button>
150
- </Flex>
151
- )}
152
- </Space>
153
- </Flex>
154
- );
155
- },
156
- [reference, fileThumbnails, handleDocumentButtonClick],
157
- );
158
-
159
- const renderReference = useCallback(
160
- (text: string) => {
161
- return reactStringReplace(text, reg, (match, i) => {
162
- const chunkIndex = getChunkIndex(match);
163
- return (
164
- <Popover content={getPopoverContent(chunkIndex)}>
165
- <InfoCircleOutlined key={i} className={styles.referenceIcon} />
166
- </Popover>
167
- );
168
- });
169
- },
170
- [getPopoverContent],
171
- );
172
-
173
  const referenceDocumentList = useMemo(() => {
174
  return reference?.doc_aggs ?? [];
175
  }, [reference?.doc_aggs]);
@@ -207,38 +84,11 @@ const MessageItem = ({
207
  <b>{isAssistant ? '' : userInfo.nickname}</b>
208
  <div className={styles.messageText}>
209
  {item.content !== '' ? (
210
- <Markdown
211
- rehypePlugins={[rehypeWrapReference]}
212
- remarkPlugins={[remarkGfm]}
213
- components={
214
- {
215
- 'custom-typography': ({
216
- children,
217
- }: {
218
- children: string;
219
- }) => renderReference(children),
220
- code(props: any) {
221
- const { children, className, node, ...rest } = props;
222
- const match = /language-(\w+)/.exec(className || '');
223
- return match ? (
224
- <SyntaxHighlighter
225
- {...rest}
226
- PreTag="div"
227
- language={match[1]}
228
- >
229
- {String(children).replace(/\n$/, '')}
230
- </SyntaxHighlighter>
231
- ) : (
232
- <code {...rest} className={className}>
233
- {children}
234
- </code>
235
- );
236
- },
237
- } as any
238
- }
239
- >
240
- {item.content}
241
- </Markdown>
242
  ) : (
243
  <Skeleton active className={styles.messageEmpty} />
244
  )}
 
1
  import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
 
2
  import NewDocumentLink from '@/components/new-document-link';
3
  import DocumentPreviewer from '@/components/pdf-previewer';
4
  import { MessageType } from '@/constants/chat';
 
6
  import { useSelectUserInfo } from '@/hooks/userSettingHook';
7
  import { IReference, Message } from '@/interfaces/database/chat';
8
  import { IChunk } from '@/interfaces/database/knowledge';
 
9
  import {
10
  Avatar,
11
  Button,
 
13
  Flex,
14
  Input,
15
  List,
 
16
  Skeleton,
 
17
  Spin,
18
  } from 'antd';
19
  import classNames from 'classnames';
20
+ import { useMemo } from 'react';
 
 
 
 
 
21
  import {
22
  useClickDrawer,
23
  useFetchConversationOnMount,
 
26
  useSelectConversationLoading,
27
  useSendMessage,
28
  } from '../hooks';
29
+ import MarkdownContent from '../markdown-content';
30
 
31
  import SvgIcon from '@/components/svg-icon';
32
  import { useTranslate } from '@/hooks/commonHooks';
33
  import { getExtension, isPdf } from '@/utils/documentUtils';
34
  import styles from './index.less';
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  const MessageItem = ({
37
  item,
38
  reference,
 
47
 
48
  const isAssistant = item.role === MessageType.Assistant;
49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  const referenceDocumentList = useMemo(() => {
51
  return reference?.doc_aggs ?? [];
52
  }, [reference?.doc_aggs]);
 
84
  <b>{isAssistant ? '' : userInfo.nickname}</b>
85
  <div className={styles.messageText}>
86
  {item.content !== '' ? (
87
+ <MarkdownContent
88
+ content={item.content}
89
+ reference={reference}
90
+ clickDocumentButton={clickDocumentButton}
91
+ ></MarkdownContent>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  ) : (
93
  <Skeleton active className={styles.messageEmpty} />
94
  )}
web/src/pages/chat/chat-overview-modal/index.tsx CHANGED
@@ -1,11 +1,15 @@
 
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 { useFetchStatsOnMount, useSelectChartStatsList } from '../hooks';
11
  import styles from './index.less';
@@ -20,6 +24,10 @@ const ChatOverviewModal = ({
20
  }: IModalProps<any> & { dialog: IDialog }) => {
21
  const { t } = useTranslate('chat');
22
  const chartList = useSelectChartStatsList();
 
 
 
 
23
 
24
  const {
25
  visible: apiKeyVisible,
@@ -45,14 +53,20 @@ const ChatOverviewModal = ({
45
  <Card title={dialog.name}>
46
  <Flex gap={8} vertical>
47
  {t('publicUrl')}
48
- <Paragraph copyable className={styles.linkText}>
49
- This is a copyable text.
50
- </Paragraph>
 
 
 
 
 
 
 
 
 
 
51
  </Flex>
52
- <Space size={'middle'}>
53
- <Button>{t('preview')}</Button>
54
- <Button>{t('embedded')}</Button>
55
- </Space>
56
  </Card>
57
  <Card title={t('backendServiceApi')}>
58
  <Flex gap={8} vertical>
 
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 { useFetchStatsOnMount, useSelectChartStatsList } from '../hooks';
15
  import styles from './index.less';
 
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,
 
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>
web/src/pages/chat/hooks.ts CHANGED
@@ -715,6 +715,8 @@ export const useGetSendButtonDisabled = () => {
715
 
716
  type RangeValue = [Dayjs | null, Dayjs | null] | null;
717
 
 
 
718
  export const useFetchStatsOnMount = (visible: boolean) => {
719
  const fetchStats = useFetchStats();
720
  const [pickerValue, setPickerValue] = useState<RangeValue>([
@@ -724,7 +726,10 @@ export const useFetchStatsOnMount = (visible: boolean) => {
724
 
725
  useEffect(() => {
726
  if (visible && Array.isArray(pickerValue) && pickerValue[0]) {
727
- fetchStats({ fromDate: pickerValue[0], toDate: pickerValue[1] });
 
 
 
728
  }
729
  }, [fetchStats, pickerValue, visible]);
730
 
 
715
 
716
  type RangeValue = [Dayjs | null, Dayjs | null] | null;
717
 
718
+ const getDay = (date: Dayjs) => date.format('YYYY-MM-DD');
719
+
720
  export const useFetchStatsOnMount = (visible: boolean) => {
721
  const fetchStats = useFetchStats();
722
  const [pickerValue, setPickerValue] = useState<RangeValue>([
 
726
 
727
  useEffect(() => {
728
  if (visible && Array.isArray(pickerValue) && pickerValue[0]) {
729
+ fetchStats({
730
+ fromDate: getDay(pickerValue[0]),
731
+ toDate: getDay(pickerValue[1] ?? dayjs()),
732
+ });
733
  }
734
  }, [fetchStats, pickerValue, visible]);
735
 
web/src/pages/chat/markdown-content/index.less ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .referencePopoverWrapper {
2
+ max-width: 50vw;
3
+ }
4
+
5
+ .referenceChunkImage {
6
+ width: 10vw;
7
+ object-fit: contain;
8
+ }
9
+
10
+ .referenceImagePreview {
11
+ max-width: 45vw;
12
+ max-height: 45vh;
13
+ }
14
+ .chunkContentText {
15
+ .chunkText;
16
+ max-height: 45vh;
17
+ overflow-y: auto;
18
+ }
19
+ .documentLink {
20
+ padding: 0;
21
+ }
22
+
23
+ .referenceIcon {
24
+ padding: 0 6px;
25
+ }
web/src/pages/chat/markdown-content/index.tsx ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Image from '@/components/image';
2
+ import SvgIcon from '@/components/svg-icon';
3
+ import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
4
+ import { IReference } from '@/interfaces/database/chat';
5
+ import { IChunk } from '@/interfaces/database/knowledge';
6
+ import { getExtension } from '@/utils/documentUtils';
7
+ import { InfoCircleOutlined } from '@ant-design/icons';
8
+ import { Button, Flex, Popover, Space } from 'antd';
9
+ import { useCallback } from 'react';
10
+ import Markdown from 'react-markdown';
11
+ import reactStringReplace from 'react-string-replace';
12
+ import SyntaxHighlighter from 'react-syntax-highlighter';
13
+ import remarkGfm from 'remark-gfm';
14
+ import { visitParents } from 'unist-util-visit-parents';
15
+
16
+ import styles from './index.less';
17
+
18
+ const reg = /(#{2}\d+\${2})/g;
19
+
20
+ const getChunkIndex = (match: string) => Number(match.slice(2, -2));
21
+ // TODO: The display of the table is inconsistent with the display previously placed in the MessageItem.
22
+ const MarkdownContent = ({
23
+ reference,
24
+ clickDocumentButton,
25
+ content,
26
+ }: {
27
+ content: string;
28
+ reference: IReference;
29
+ clickDocumentButton: (documentId: string, chunk: IChunk) => void;
30
+ }) => {
31
+ const fileThumbnails = useSelectFileThumbnails();
32
+
33
+ const handleDocumentButtonClick = useCallback(
34
+ (documentId: string, chunk: IChunk, isPdf: boolean) => () => {
35
+ if (!isPdf) {
36
+ return;
37
+ }
38
+ clickDocumentButton(documentId, chunk);
39
+ },
40
+ [clickDocumentButton],
41
+ );
42
+
43
+ const rehypeWrapReference = () => {
44
+ return function wrapTextTransform(tree: any) {
45
+ visitParents(tree, 'text', (node, ancestors) => {
46
+ const latestAncestor = ancestors.at(-1);
47
+ if (
48
+ latestAncestor.tagName !== 'custom-typography' &&
49
+ latestAncestor.tagName !== 'code'
50
+ ) {
51
+ node.type = 'element';
52
+ node.tagName = 'custom-typography';
53
+ node.properties = {};
54
+ node.children = [{ type: 'text', value: node.value }];
55
+ }
56
+ });
57
+ };
58
+ };
59
+
60
+ const getPopoverContent = useCallback(
61
+ (chunkIndex: number) => {
62
+ const chunks = reference?.chunks ?? [];
63
+ const chunkItem = chunks[chunkIndex];
64
+ const document = reference?.doc_aggs.find(
65
+ (x) => x?.doc_id === chunkItem?.doc_id,
66
+ );
67
+ const documentId = document?.doc_id;
68
+ const fileThumbnail = documentId ? fileThumbnails[documentId] : '';
69
+ const fileExtension = documentId ? getExtension(document?.doc_name) : '';
70
+ const imageId = chunkItem?.img_id;
71
+ return (
72
+ <Flex
73
+ key={chunkItem?.chunk_id}
74
+ gap={10}
75
+ className={styles.referencePopoverWrapper}
76
+ >
77
+ {imageId && (
78
+ <Popover
79
+ placement="left"
80
+ content={
81
+ <Image
82
+ id={imageId}
83
+ className={styles.referenceImagePreview}
84
+ ></Image>
85
+ }
86
+ >
87
+ <Image
88
+ id={imageId}
89
+ className={styles.referenceChunkImage}
90
+ ></Image>
91
+ </Popover>
92
+ )}
93
+ <Space direction={'vertical'}>
94
+ <div
95
+ dangerouslySetInnerHTML={{
96
+ __html: chunkItem?.content_with_weight,
97
+ }}
98
+ className={styles.chunkContentText}
99
+ ></div>
100
+ {documentId && (
101
+ <Flex gap={'small'}>
102
+ {fileThumbnail ? (
103
+ <img src={fileThumbnail} alt="" />
104
+ ) : (
105
+ <SvgIcon
106
+ name={`file-icon/${fileExtension}`}
107
+ width={24}
108
+ ></SvgIcon>
109
+ )}
110
+ <Button
111
+ type="link"
112
+ className={styles.documentLink}
113
+ onClick={handleDocumentButtonClick(
114
+ documentId,
115
+ chunkItem,
116
+ fileExtension === 'pdf',
117
+ )}
118
+ >
119
+ {document?.doc_name}
120
+ </Button>
121
+ </Flex>
122
+ )}
123
+ </Space>
124
+ </Flex>
125
+ );
126
+ },
127
+ [reference, fileThumbnails, handleDocumentButtonClick],
128
+ );
129
+
130
+ const renderReference = useCallback(
131
+ (text: string) => {
132
+ return reactStringReplace(text, reg, (match, i) => {
133
+ const chunkIndex = getChunkIndex(match);
134
+ return (
135
+ <Popover content={getPopoverContent(chunkIndex)}>
136
+ <InfoCircleOutlined key={i} className={styles.referenceIcon} />
137
+ </Popover>
138
+ );
139
+ });
140
+ },
141
+ [getPopoverContent],
142
+ );
143
+
144
+ return (
145
+ <Markdown
146
+ rehypePlugins={[rehypeWrapReference]}
147
+ remarkPlugins={[remarkGfm]}
148
+ components={
149
+ {
150
+ 'custom-typography': ({ children }: { children: string }) =>
151
+ renderReference(children),
152
+ code(props: any) {
153
+ const { children, className, node, ...rest } = props;
154
+ const match = /language-(\w+)/.exec(className || '');
155
+ return match ? (
156
+ <SyntaxHighlighter {...rest} PreTag="div" language={match[1]}>
157
+ {String(children).replace(/\n$/, '')}
158
+ </SyntaxHighlighter>
159
+ ) : (
160
+ <code {...rest} className={className}>
161
+ {children}
162
+ </code>
163
+ );
164
+ },
165
+ } as any
166
+ }
167
+ >
168
+ {content}
169
+ </Markdown>
170
+ );
171
+ };
172
+
173
+ export default MarkdownContent;
web/src/pages/chat/model.ts CHANGED
@@ -158,7 +158,7 @@ const model: DvaModel<ChatModelState> = {
158
  }
159
  return data;
160
  },
161
- *completeConversation({ payload }, { call, put }) {
162
  const { data } = yield call(chatService.completeConversation, payload);
163
  // if (data.retcode === 0) {
164
  // yield put({
@@ -192,7 +192,7 @@ const model: DvaModel<ChatModelState> = {
192
  });
193
  message.success(i18n.t('message.created'));
194
  }
195
- return data.retcode;
196
  },
197
  *listToken({ payload }, { call, put }) {
198
  const { data } = yield call(chatService.listToken, payload);
@@ -232,13 +232,13 @@ const model: DvaModel<ChatModelState> = {
232
  chatService.createExternalConversation,
233
  payload,
234
  );
235
- if (data.retcode === 0) {
236
- yield put({
237
- type: 'getExternalConversation',
238
- payload: { conversation_id: payload.conversationId },
239
- });
240
- }
241
- return data.retcode;
242
  },
243
  *getExternalConversation({ payload }, { call }) {
244
  const { data } = yield call(
@@ -246,7 +246,7 @@ const model: DvaModel<ChatModelState> = {
246
  null,
247
  payload,
248
  );
249
- return data.retcode;
250
  },
251
  *completeExternalConversation({ payload }, { call }) {
252
  const { data } = yield call(
 
158
  }
159
  return data;
160
  },
161
+ *completeConversation({ payload }, { call }) {
162
  const { data } = yield call(chatService.completeConversation, payload);
163
  // if (data.retcode === 0) {
164
  // yield put({
 
192
  });
193
  message.success(i18n.t('message.created'));
194
  }
195
+ return data;
196
  },
197
  *listToken({ payload }, { call, put }) {
198
  const { data } = yield call(chatService.listToken, payload);
 
232
  chatService.createExternalConversation,
233
  payload,
234
  );
235
+ // if (data.retcode === 0) {
236
+ // yield put({
237
+ // type: 'getExternalConversation',
238
+ // payload: data.data.id,
239
+ // });
240
+ // }
241
+ return data;
242
  },
243
  *getExternalConversation({ payload }, { call }) {
244
  const { data } = yield call(
 
246
  null,
247
  payload,
248
  );
249
+ return data;
250
  },
251
  *completeExternalConversation({ payload }, { call }) {
252
  const { data } = yield call(
web/src/pages/chat/share/index.less ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .chatWrapper {
2
+ height: 100%;
3
+ }
4
+
5
+ .chatContainer {
6
+ padding: 10px;
7
+ box-sizing: border-box;
8
+ height: 100%;
9
+ .messageContainer {
10
+ overflow-y: auto;
11
+ padding-right: 6px;
12
+ }
13
+ }
14
+
15
+ .messageItem {
16
+ padding: 24px 0;
17
+ .messageItemSection {
18
+ display: inline-block;
19
+ }
20
+ .messageItemSectionLeft {
21
+ width: 70%;
22
+ }
23
+ .messageItemSectionRight {
24
+ width: 40%;
25
+ }
26
+ .messageItemContent {
27
+ display: inline-flex;
28
+ gap: 20px;
29
+ }
30
+ .messageItemContentReverse {
31
+ flex-direction: row-reverse;
32
+ }
33
+ .messageText {
34
+ .chunkText();
35
+ padding: 0 14px;
36
+ background-color: rgba(249, 250, 251, 1);
37
+ word-break: break-all;
38
+ }
39
+ .messageEmpty {
40
+ width: 300px;
41
+ }
42
+ }
43
+
44
+ .messageItemLeft {
45
+ text-align: left;
46
+ }
47
+
48
+ .messageItemRight {
49
+ text-align: right;
50
+ }
web/src/pages/chat/share/index.tsx ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect } from 'react';
2
+ import {
3
+ useCreateSharedConversationOnMount,
4
+ useSelectCurrentSharedConversation,
5
+ useSendSharedMessage,
6
+ } from '../shared-hooks';
7
+ import ChatContainer from './large';
8
+
9
+ import styles from './index.less';
10
+
11
+ const SharedChat = () => {
12
+ const { conversationId } = useCreateSharedConversationOnMount();
13
+ const {
14
+ currentConversation,
15
+ addNewestConversation,
16
+ removeLatestMessage,
17
+ ref,
18
+ loading,
19
+ setCurrentConversation,
20
+ } = useSelectCurrentSharedConversation(conversationId);
21
+
22
+ const {
23
+ handlePressEnter,
24
+ handleInputChange,
25
+ value,
26
+ loading: sendLoading,
27
+ } = useSendSharedMessage(
28
+ currentConversation,
29
+ addNewestConversation,
30
+ removeLatestMessage,
31
+ setCurrentConversation,
32
+ );
33
+
34
+ useEffect(() => {
35
+ console.info(location.href);
36
+ }, []);
37
+
38
+ return (
39
+ <div className={styles.chatWrapper}>
40
+ <ChatContainer
41
+ value={value}
42
+ handleInputChange={handleInputChange}
43
+ handlePressEnter={handlePressEnter}
44
+ loading={loading}
45
+ sendLoading={sendLoading}
46
+ conversation={currentConversation}
47
+ ref={ref}
48
+ ></ChatContainer>
49
+ </div>
50
+ );
51
+ };
52
+
53
+ export default SharedChat;
web/src/pages/chat/share/large.tsx ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
2
+ import { MessageType } from '@/constants/chat';
3
+ import { useTranslate } from '@/hooks/commonHooks';
4
+ import { Message } from '@/interfaces/database/chat';
5
+ 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;
16
+
17
+ return (
18
+ <div
19
+ className={classNames(styles.messageItem, {
20
+ [styles.messageItemLeft]: item.role === MessageType.Assistant,
21
+ [styles.messageItemRight]: item.role === MessageType.User,
22
+ })}
23
+ >
24
+ <section
25
+ className={classNames(styles.messageItemSection, {
26
+ [styles.messageItemSectionLeft]: item.role === MessageType.Assistant,
27
+ [styles.messageItemSectionRight]: item.role === MessageType.User,
28
+ })}
29
+ >
30
+ <div
31
+ className={classNames(styles.messageItemContent, {
32
+ [styles.messageItemContentReverse]: item.role === MessageType.User,
33
+ })}
34
+ >
35
+ {item.role === MessageType.User ? (
36
+ <Avatar
37
+ size={40}
38
+ src={
39
+ 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
40
+ }
41
+ />
42
+ ) : (
43
+ <AssistantIcon></AssistantIcon>
44
+ )}
45
+ <Flex vertical gap={8} flex={1}>
46
+ <b>{isAssistant ? '' : 'You'}</b>
47
+ <div className={styles.messageText}>
48
+ {item.content !== '' ? (
49
+ <SharedMarkdown content={item.content}></SharedMarkdown>
50
+ ) : (
51
+ <Skeleton active className={styles.messageEmpty} />
52
+ )}
53
+ </div>
54
+ </Flex>
55
+ </div>
56
+ </section>
57
+ </div>
58
+ );
59
+ };
60
+
61
+ interface IProps {
62
+ handlePressEnter(): void;
63
+ handleInputChange: ChangeEventHandler<HTMLInputElement>;
64
+ value: string;
65
+ loading: boolean;
66
+ sendLoading: boolean;
67
+ conversation: IClientConversation;
68
+ ref: React.LegacyRef<any>;
69
+ }
70
+
71
+ const ChatContainer = (
72
+ {
73
+ handlePressEnter,
74
+ handleInputChange,
75
+ value,
76
+ loading: sendLoading,
77
+ conversation,
78
+ }: IProps,
79
+ ref: React.LegacyRef<any>,
80
+ ) => {
81
+ const loading = useSelectConversationLoading();
82
+ const { t } = useTranslate('chat');
83
+
84
+ return (
85
+ <>
86
+ <Flex flex={1} className={styles.chatContainer} vertical>
87
+ <Flex flex={1} vertical className={styles.messageContainer}>
88
+ <div>
89
+ <Spin spinning={loading}>
90
+ {conversation?.message?.map((message) => {
91
+ return (
92
+ <MessageItem key={message.id} item={message}></MessageItem>
93
+ );
94
+ })}
95
+ </Spin>
96
+ </div>
97
+ <div ref={ref} />
98
+ </Flex>
99
+ <Input
100
+ size="large"
101
+ placeholder={t('sendPlaceholder')}
102
+ value={value}
103
+ // disabled={disabled}
104
+ suffix={
105
+ <Button
106
+ type="primary"
107
+ onClick={handlePressEnter}
108
+ loading={sendLoading}
109
+ // disabled={disabled}
110
+ >
111
+ {t('send')}
112
+ </Button>
113
+ }
114
+ onPressEnter={handlePressEnter}
115
+ onChange={handleInputChange}
116
+ />
117
+ </Flex>
118
+ </>
119
+ );
120
+ };
121
+
122
+ export default forwardRef(ChatContainer);
web/src/pages/chat/share/shared-markdown.tsx ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Markdown from 'react-markdown';
2
+ import SyntaxHighlighter from 'react-syntax-highlighter';
3
+ import remarkGfm from 'remark-gfm';
4
+
5
+ const SharedMarkdown = ({ content }: { content: string }) => {
6
+ return (
7
+ <Markdown
8
+ remarkPlugins={[remarkGfm]}
9
+ components={
10
+ {
11
+ code(props: any) {
12
+ const { children, className, node, ...rest } = props;
13
+ const match = /language-(\w+)/.exec(className || '');
14
+ return match ? (
15
+ <SyntaxHighlighter {...rest} PreTag="div" language={match[1]}>
16
+ {String(children).replace(/\n$/, '')}
17
+ </SyntaxHighlighter>
18
+ ) : (
19
+ <code {...rest} className={className}>
20
+ {children}
21
+ </code>
22
+ );
23
+ },
24
+ } as any
25
+ }
26
+ >
27
+ {content}
28
+ </Markdown>
29
+ );
30
+ };
31
+
32
+ export default SharedMarkdown;
web/src/pages/chat/shared-hooks.ts ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { MessageType } from '@/constants/chat';
2
+ import {
3
+ useCompleteSharedConversation,
4
+ useCreateSharedConversation,
5
+ useFetchSharedConversation,
6
+ } from '@/hooks/chatHooks';
7
+ import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
8
+ import omit from 'lodash/omit';
9
+ import {
10
+ Dispatch,
11
+ SetStateAction,
12
+ useCallback,
13
+ useEffect,
14
+ useState,
15
+ } from 'react';
16
+ import { useSearchParams } from 'umi';
17
+ import { v4 as uuid } from 'uuid';
18
+ import { useHandleMessageInputChange, useScrollToBottom } from './hooks';
19
+ import { IClientConversation, IMessage } from './interface';
20
+
21
+ export const useCreateSharedConversationOnMount = () => {
22
+ const [currentQueryParameters] = useSearchParams();
23
+ const [conversationId, setConversationId] = useState('');
24
+
25
+ const createConversation = useCreateSharedConversation();
26
+ const sharedId = currentQueryParameters.get('shared_id');
27
+ const userId = currentQueryParameters.get('user_id');
28
+
29
+ const setConversation = useCallback(async () => {
30
+ console.info(sharedId);
31
+ if (sharedId) {
32
+ const data = await createConversation(userId ?? undefined);
33
+ const id = data.data?.id;
34
+ if (id) {
35
+ setConversationId(id);
36
+ }
37
+ }
38
+ }, [createConversation, sharedId, userId]);
39
+
40
+ useEffect(() => {
41
+ setConversation();
42
+ }, [setConversation]);
43
+
44
+ return { conversationId };
45
+ };
46
+
47
+ export const useSelectCurrentSharedConversation = (conversationId: string) => {
48
+ const [currentConversation, setCurrentConversation] =
49
+ useState<IClientConversation>({} as IClientConversation);
50
+ const fetchConversation = useFetchSharedConversation();
51
+ const loading = useOneNamespaceEffectsLoading('chatModel', [
52
+ 'getExternalConversation',
53
+ ]);
54
+
55
+ const ref = useScrollToBottom(currentConversation);
56
+
57
+ const addNewestConversation = useCallback((message: string) => {
58
+ setCurrentConversation((pre) => {
59
+ return {
60
+ ...pre,
61
+ message: [
62
+ ...(pre.message ?? []),
63
+ {
64
+ role: MessageType.User,
65
+ content: message,
66
+ id: uuid(),
67
+ } as IMessage,
68
+ {
69
+ role: MessageType.Assistant,
70
+ content: '',
71
+ id: uuid(),
72
+ reference: [],
73
+ } as IMessage,
74
+ ],
75
+ };
76
+ });
77
+ }, []);
78
+
79
+ const removeLatestMessage = useCallback(() => {
80
+ setCurrentConversation((pre) => {
81
+ const nextMessages = pre.message.slice(0, -2);
82
+ return {
83
+ ...pre,
84
+ message: nextMessages,
85
+ };
86
+ });
87
+ }, []);
88
+
89
+ const fetchConversationOnMount = useCallback(async () => {
90
+ if (conversationId) {
91
+ const data = await fetchConversation(conversationId);
92
+ if (data.retcode === 0) {
93
+ setCurrentConversation(data.data);
94
+ }
95
+ }
96
+ }, [conversationId, fetchConversation]);
97
+
98
+ useEffect(() => {
99
+ fetchConversationOnMount();
100
+ }, [fetchConversationOnMount]);
101
+
102
+ return {
103
+ currentConversation,
104
+ addNewestConversation,
105
+ removeLatestMessage,
106
+ loading,
107
+ ref,
108
+ setCurrentConversation,
109
+ };
110
+ };
111
+
112
+ export const useSendSharedMessage = (
113
+ conversation: IClientConversation,
114
+ addNewestConversation: (message: string) => void,
115
+ removeLatestMessage: () => void,
116
+ setCurrentConversation: Dispatch<SetStateAction<IClientConversation>>,
117
+ ) => {
118
+ const conversationId = conversation.id;
119
+ const loading = useOneNamespaceEffectsLoading('chatModel', [
120
+ 'completeExternalConversation',
121
+ ]);
122
+ const setConversation = useCreateSharedConversation();
123
+ const { handleInputChange, value, setValue } = useHandleMessageInputChange();
124
+
125
+ const fetchConversation = useFetchSharedConversation();
126
+ const completeConversation = useCompleteSharedConversation();
127
+
128
+ const sendMessage = useCallback(
129
+ async (message: string, id?: string) => {
130
+ const retcode = await completeConversation({
131
+ conversation_id: id ?? conversationId,
132
+ messages: [
133
+ ...(conversation?.message ?? []).map((x: IMessage) => omit(x, 'id')),
134
+ {
135
+ role: MessageType.User,
136
+ content: message,
137
+ },
138
+ ],
139
+ });
140
+
141
+ if (retcode === 0) {
142
+ const data = await fetchConversation(conversationId);
143
+ if (data.retcode === 0) {
144
+ setCurrentConversation(data.data);
145
+ }
146
+ } else {
147
+ // cancel loading
148
+ setValue(message);
149
+ removeLatestMessage();
150
+ }
151
+ },
152
+ [
153
+ conversationId,
154
+ conversation?.message,
155
+ fetchConversation,
156
+ removeLatestMessage,
157
+ setValue,
158
+ completeConversation,
159
+ setCurrentConversation,
160
+ ],
161
+ );
162
+
163
+ const handleSendMessage = useCallback(
164
+ async (message: string) => {
165
+ if (conversationId !== '') {
166
+ sendMessage(message);
167
+ } else {
168
+ const data = await setConversation('user id');
169
+ if (data.retcode === 0) {
170
+ const id = data.data.id;
171
+ sendMessage(message, id);
172
+ }
173
+ }
174
+ },
175
+ [conversationId, setConversation, sendMessage],
176
+ );
177
+
178
+ const handlePressEnter = () => {
179
+ if (!loading) {
180
+ setValue('');
181
+ addNewestConversation(value);
182
+ handleSendMessage(value.trim());
183
+ }
184
+ };
185
+
186
+ return {
187
+ handlePressEnter,
188
+ handleInputChange,
189
+ value,
190
+ loading,
191
+ };
192
+ };
web/src/routes.ts CHANGED
@@ -4,6 +4,11 @@ const routes = [
4
  component: '@/pages/login',
5
  layout: false,
6
  },
 
 
 
 
 
7
  {
8
  path: '/',
9
  component: '@/layouts',
 
4
  component: '@/pages/login',
5
  layout: false,
6
  },
7
+ {
8
+ path: '/chat/share',
9
+ component: '@/pages/chat/share',
10
+ layout: false,
11
+ },
12
  {
13
  path: '/',
14
  component: '@/layouts',
web/src/services/chatService.ts CHANGED
@@ -76,7 +76,7 @@ const methods = {
76
  },
77
  createExternalConversation: {
78
  url: createExternalConversation,
79
- method: 'post',
80
  },
81
  getExternalConversation: {
82
  url: getExternalConversation,
 
76
  },
77
  createExternalConversation: {
78
  url: createExternalConversation,
79
+ method: 'get',
80
  },
81
  getExternalConversation: {
82
  url: getExternalConversation,
web/src/utils/commonUtil.ts CHANGED
@@ -15,3 +15,8 @@ export const convertTheKeysOfTheObjectToSnake = (data: unknown) => {
15
  }
16
  return data;
17
  };
 
 
 
 
 
 
15
  }
16
  return data;
17
  };
18
+
19
+ export const getSearchValue = (key: string) => {
20
+ const params = new URL(document.location as any).searchParams;
21
+ return params.get(key);
22
+ };
web/src/utils/request.ts CHANGED
@@ -4,7 +4,7 @@ import authorizationUtil from '@/utils/authorizationUtil';
4
  import { message, notification } from 'antd';
5
  import { history } from 'umi';
6
  import { RequestMethod, extend } from 'umi-request';
7
- import { convertTheKeysOfTheObjectToSnake } from './commonUtil';
8
 
9
  const ABORT_REQUEST_ERR_MESSAGE = 'The user aborted a request.'; // 手动中断请求。errorHandler 抛出的error message
10
 
@@ -87,7 +87,10 @@ const request: RequestMethod = extend({
87
  });
88
 
89
  request.interceptors.request.use((url: string, options: any) => {
90
- const authorization = authorizationUtil.getAuthorization();
 
 
 
91
  const data = convertTheKeysOfTheObjectToSnake(options.data);
92
  const params = convertTheKeysOfTheObjectToSnake(options.params);
93
 
 
4
  import { message, notification } from 'antd';
5
  import { history } from 'umi';
6
  import { RequestMethod, extend } from 'umi-request';
7
+ import { convertTheKeysOfTheObjectToSnake, getSearchValue } from './commonUtil';
8
 
9
  const ABORT_REQUEST_ERR_MESSAGE = 'The user aborted a request.'; // 手动中断请求。errorHandler 抛出的error message
10
 
 
87
  });
88
 
89
  request.interceptors.request.use((url: string, options: any) => {
90
+ const sharedId = getSearchValue('shared_id');
91
+ const authorization = sharedId
92
+ ? 'Bearer ' + sharedId
93
+ : authorizationUtil.getAuthorization();
94
  const data = convertTheKeysOfTheObjectToSnake(options.data);
95
  const params = convertTheKeysOfTheObjectToSnake(options.params);
96