Resource.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import { apiInterceptors, postChatModeParamsFileLoad, postChatModeParamsList } from '@/client/api';
  2. import DBIcon from '@/components/common/db-icon';
  3. import { ChatContentContext } from '@/pages/chat';
  4. import { IDB } from '@/types/chat';
  5. import { dbMapper } from '@/utils';
  6. import { ExperimentOutlined, FolderAddOutlined } from '@ant-design/icons';
  7. import { useAsyncEffect, useRequest } from 'ahooks';
  8. import type { UploadFile } from 'antd';
  9. import { Select, Tooltip, Upload } from 'antd';
  10. import classNames from 'classnames';
  11. import { useSearchParams } from 'next/navigation';
  12. import React, { memo, useCallback, useContext, useMemo, useState } from 'react';
  13. import { useTranslation } from 'react-i18next';
  14. const Resource: React.FC<{
  15. fileList: UploadFile[];
  16. setFileList: React.Dispatch<React.SetStateAction<UploadFile<any>[]>>;
  17. setLoading: React.Dispatch<React.SetStateAction<boolean>>;
  18. fileName: string;
  19. }> = ({ fileList, setFileList, setLoading, fileName }) => {
  20. const { setResourceValue, appInfo, refreshHistory, refreshDialogList, modelValue, resourceValue } =
  21. useContext(ChatContentContext);
  22. const searchParams = useSearchParams();
  23. const scene = searchParams?.get('scene') ?? '';
  24. const chatId = searchParams?.get('id') ?? '';
  25. const { t } = useTranslation();
  26. // dataBase
  27. const [dbs, setDbs] = useState<IDB[]>([]);
  28. // 左边工具栏动态可用key
  29. const paramKey: string[] = useMemo(() => {
  30. return appInfo.param_need?.map(i => i.type) || [];
  31. }, [appInfo.param_need]);
  32. const isDataBase = useMemo(() => {
  33. return (
  34. paramKey.includes('resource') && appInfo.param_need?.filter(i => i.type === 'resource')[0]?.value === 'database'
  35. );
  36. }, [appInfo.param_need, paramKey]);
  37. const isKnowledge = useMemo(() => {
  38. return (
  39. paramKey.includes('resource') && appInfo.param_need?.filter(i => i.type === 'resource')[0]?.value === 'knowledge'
  40. );
  41. }, [appInfo.param_need, paramKey]);
  42. const resource = useMemo(() => appInfo.param_need?.find(i => i.type === 'resource'), [appInfo.param_need]);
  43. // 获取db
  44. const { run, loading } = useRequest(async () => await apiInterceptors(postChatModeParamsList(scene as string)), {
  45. manual: true,
  46. onSuccess: data => {
  47. const [, res] = data;
  48. setDbs(res ?? []);
  49. },
  50. });
  51. useAsyncEffect(async () => {
  52. if ((isDataBase || isKnowledge) && !resource?.bind_value) {
  53. await run();
  54. }
  55. }, [isDataBase, isKnowledge, resource]);
  56. const dbOpts = useMemo(
  57. () =>
  58. dbs.map?.((db: IDB) => {
  59. return {
  60. label: (
  61. <>
  62. <DBIcon
  63. width={24}
  64. height={24}
  65. src={dbMapper[db.type].icon}
  66. label={dbMapper[db.type].label}
  67. className='w-[1.5em] h-[1.5em] mr-1 inline-block mt-[-4px]'
  68. />
  69. {db.param}
  70. </>
  71. ),
  72. value: db.param,
  73. };
  74. }),
  75. [dbs],
  76. );
  77. // 上传
  78. const onUpload = useCallback(async () => {
  79. const formData = new FormData();
  80. formData.append('doc_file', fileList?.[0] as any);
  81. setLoading(true);
  82. const [_, res] = await apiInterceptors(
  83. postChatModeParamsFileLoad({
  84. convUid: chatId,
  85. chatMode: scene,
  86. data: formData,
  87. model: modelValue,
  88. config: {
  89. timeout: 1000 * 60 * 60,
  90. },
  91. }),
  92. ).finally(() => {
  93. setLoading(false);
  94. });
  95. if (res) {
  96. setResourceValue(res);
  97. await refreshHistory();
  98. await refreshDialogList();
  99. }
  100. }, [chatId, fileList, modelValue, refreshDialogList, refreshHistory, scene, setLoading, setResourceValue]);
  101. if (!paramKey.includes('resource')) {
  102. return (
  103. <Tooltip title={t('extend_tip')}>
  104. <div className='flex w-8 h-8 items-center justify-center rounded-md hover:bg-[rgb(221,221,221,0.6)]'>
  105. <ExperimentOutlined className='text-lg cursor-not-allowed opacity-30' />
  106. </div>
  107. </Tooltip>
  108. );
  109. }
  110. switch (resource?.value) {
  111. case 'excel_file':
  112. case 'text_file':
  113. case 'image_file':
  114. return (
  115. <Upload
  116. name='file'
  117. accept='.csv,.xlsx,.xls'
  118. fileList={fileList}
  119. showUploadList={false}
  120. beforeUpload={(_, fileList) => {
  121. setFileList?.(fileList);
  122. }}
  123. customRequest={onUpload}
  124. disabled={!!fileName || !!fileList[0]?.name}
  125. >
  126. <Tooltip title={t('file_tip')} arrow={false} placement='bottom'>
  127. <div className='flex w-8 h-8 items-center justify-center rounded-md hover:bg-[rgb(221,221,221,0.6)]'>
  128. <FolderAddOutlined
  129. className={classNames('text-xl', { 'cursor-pointer': !(!!fileName || !!fileList[0]?.name) })}
  130. />
  131. </div>
  132. </Tooltip>
  133. </Upload>
  134. );
  135. case 'database':
  136. case 'knowledge':
  137. case 'plugin':
  138. case 'awel_flow':
  139. if (!resourceValue) {
  140. setResourceValue(dbOpts?.[0]?.value);
  141. }
  142. return (
  143. <Select
  144. value={resourceValue}
  145. className='w-52 h-8 rounded-3xl'
  146. onChange={val => {
  147. setResourceValue(val);
  148. }}
  149. disabled={!!resource?.bind_value}
  150. loading={loading}
  151. options={dbOpts}
  152. />
  153. );
  154. }
  155. };
  156. export default memo(Resource);