drawerIndex.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. import * as React from 'react';
  2. import { generatePath, useParams, useLocation } from 'react-router-dom';
  3. import { observer } from 'mobx-react';
  4. import config, { getHeaders } from '@/apis/config';
  5. import {
  6. Button,
  7. Table,
  8. TableColumnsType,
  9. Modal,
  10. TablePaginationConfig,
  11. Upload,
  12. UploadProps,
  13. message,
  14. Spin
  15. } from 'antd';
  16. import { EditOutlined, DeleteOutlined, InboxOutlined, PlusOutlined, ArrowLeftOutlined } from '@ant-design/icons';
  17. import InfoModal from './components/InfoModal';
  18. import InfoModalSetting from './components/InfoModalSetting';
  19. import router from '@/router';
  20. import { Record } from './types';
  21. import dayjs from 'dayjs';
  22. import axios from 'axios';
  23. import LocalStorage from '@/LocalStorage';
  24. import store from './store';
  25. import './style.less';
  26. const { Dragger } = Upload;
  27. interface Props{
  28. drawerItem: any;
  29. }
  30. const KnowledgeLibInfo : React.FC<Props> = ({drawerItem}:Props) => {
  31. const params = {
  32. knowledgeId: drawerItem.value,
  33. ...drawerItem,
  34. }
  35. const {
  36. state,
  37. init,
  38. onClickModify,
  39. onClickDelete,
  40. onChangePagination,
  41. onClickDocumentDetail,
  42. infoModalOnClickConfirm,
  43. infoModalOnClickCancel,
  44. infoModalSettingOnClickConfirm,
  45. infoModalSettingOnClickCancel,
  46. onClickSettings,
  47. reset
  48. } = store;
  49. const {
  50. knowledge_id,
  51. listLoading,
  52. page,
  53. list,
  54. infoModalOpen,
  55. infoModalId,
  56. infoModalSettingOpen,
  57. infoModalSettingId,
  58. knowledgeDetail,
  59. } = state;
  60. const location = useLocation();
  61. const [ uploadLoading, setUploadLoading ] = React.useState( false );
  62. const [ fileList, setFileList ] = React.useState<any[]>( [] );
  63. const [ uploading, setUploading ] = React.useState( false );
  64. const [ sListFlag, setSListFlag ] = React.useState<boolean>();
  65. const [ cUpdateFlag, setCUpdateFlag ] = React.useState<boolean>();
  66. const [ detailFlag, setDetailFlag ] = React.useState<boolean>();
  67. const [ deleteFlag, setDeleteFlag ] = React.useState<boolean>();
  68. const [ createFlag, setCreateFlag ] = React.useState<boolean>();
  69. const [userInfoAll, setUserInfoAll] = React.useState<any>({});
  70. const props : UploadProps = {
  71. name: 'files',
  72. multiple: true,
  73. action: '/api/deepseek/api/uploadDocument/' + params.knowledgeId,
  74. headers: getHeaders(),
  75. beforeUpload( file, fileList ) {
  76. setUploadLoading( true );
  77. // const allowedExtensions = ['md', 'txt', 'pdf', 'jpg', 'png', 'jpeg', 'docx', 'xlsx', 'pptx', 'eml', 'csv', 'tar', 'gz', 'bz2', 'zip', 'rar', 'jar'];
  78. const allowedExtensions = [ 'txt', 'pdf', 'jpg', 'png', 'jpeg', 'doc', 'docx', 'ppt', 'pptx' ];
  79. // 检查文件类型
  80. for ( const file of fileList ) {
  81. const fileExt = file.name.split( '.' ).pop()?.toLowerCase();
  82. if ( !fileExt || !allowedExtensions.includes( fileExt ) ) {
  83. message.error( `不支持 ${ fileExt } 格式的文件上传` );
  84. setUploadLoading( false );
  85. return Upload.LIST_IGNORE;
  86. }
  87. }
  88. // 检查文件大小
  89. let totalSize = 0;
  90. for ( const file of fileList ) {
  91. const fileExt = file.name.split( '.' ).pop()?.toLowerCase();
  92. const fileSizeMB = file.size / 1024 / 1024;
  93. if ( fileSizeMB > 30 ) {
  94. message.error( '单个文件不能大于30M' );
  95. setUploadLoading( false );
  96. return Upload.LIST_IGNORE;
  97. }
  98. if ( [ 'jpg', 'png', 'jpeg' ].includes( fileExt! ) && fileSizeMB > 5 ) {
  99. message.error( '单张图片不能大于5M' );
  100. setUploadLoading( false );
  101. return Upload.LIST_IGNORE;
  102. }
  103. totalSize += fileSizeMB;
  104. }
  105. if ( totalSize > 125 ) {
  106. message.error( '文件总大小超过125M' );
  107. setUploadLoading( false );
  108. return Upload.LIST_IGNORE;
  109. }
  110. },
  111. onChange( info ) {
  112. const { status } = info.file;
  113. if ( status !== 'uploading' ) {
  114. console.log( status, 'status--uploading' );
  115. }
  116. if ( status === 'done' ) {
  117. console.log( status, 'status--done' );
  118. console.info( info.file.response, 'info.file.response.data' );
  119. if ( info.file.response.code === 200 && info.file.response.data === 1 ) {
  120. message.success( `${ info.file.name } file uploaded successfully.` );
  121. init( params.knowledgeId );
  122. }
  123. setUploadLoading( false );
  124. } else if ( status === 'error' ) {
  125. console.log( status, 'status--error' );
  126. message.error( `${ info.file.name } file upload failed.` );
  127. setUploadLoading( false );
  128. }
  129. },
  130. onDrop( e ) {
  131. console.log( 'Dropped files', e.dataTransfer.files );
  132. },
  133. };
  134. const handleUpload = async () => {
  135. if ( fileList.length === 0 ) return;
  136. setUploading( true );
  137. const formData = new FormData();
  138. // 添加所有文件
  139. fileList.forEach( file => {
  140. if ( file.originFileObj ) {
  141. formData.append( 'files', file.originFileObj );
  142. }
  143. } );
  144. try {
  145. const res = await axios.post( '/api/deepseek/api/uploadDocument/' + params.knowledgeId, formData, {
  146. headers: { 'Content-Type': 'multipart/form-data' }
  147. } );
  148. message.success( `${ fileList.length }个文件上传成功` );
  149. setFileList( [] );
  150. } catch ( err ) {
  151. message.error( '上传失败' );
  152. } finally {
  153. setUploading( false );
  154. }
  155. };
  156. React.useEffect( () => {
  157. init( params.knowledgeId );
  158. const cList = LocalStorage.getStatusFlag( 'deepseek:slice:list' );
  159. setSListFlag( cList );
  160. const cDetail = LocalStorage.getStatusFlag( 'deepseek:config:update' );
  161. setCUpdateFlag( cDetail );
  162. const detail = LocalStorage.getStatusFlag( 'deepseek:document:detail' );
  163. setDetailFlag( detail );
  164. const deleteF = LocalStorage.getStatusFlag( 'deepseek:document:delete' );
  165. setDeleteFlag( deleteF );
  166. const createF = LocalStorage.getStatusFlag( 'deepseek:document:create' );
  167. setCreateFlag( createF );
  168. setUserInfoAll(LocalStorage.getUserInfo());
  169. return () => reset();
  170. }, [params.knowledgeId] );
  171. const columns : TableColumnsType<Record> = [
  172. {
  173. title: '序号',
  174. dataIndex: 'index',
  175. width: 80,
  176. render: ( text, record, index ) => {
  177. return index + 1;
  178. }
  179. },
  180. {
  181. title: '文件名',
  182. dataIndex: 'name',
  183. width: 300,
  184. sorter: (a, b) => a.name.localeCompare(b.name),
  185. render: ( text, record ) => {
  186. return (
  187. `${ text }`
  188. )
  189. }
  190. },
  191. {
  192. title: '文件大小',
  193. dataIndex: 'length',
  194. width: 100,
  195. render: ( text ) => {
  196. if ( text ) {
  197. const size = ( text / 1024 / 1024 ).toFixed( 2 );
  198. return `${ size } M`;
  199. } else {
  200. return '--'
  201. }
  202. }
  203. },
  204. {
  205. title: '字符数量',
  206. dataIndex: 'wordNum',
  207. width: 100,
  208. render: ( text ) => {
  209. if ( text ) {
  210. return `${ text }`;
  211. } else {
  212. return '--'
  213. }
  214. }
  215. },
  216. {
  217. title: '分段',
  218. dataIndex: 'sliceTotal',
  219. width: 100,
  220. render: ( text ) => {
  221. if ( text ) {
  222. return `${ text }`;
  223. } else {
  224. return '--';
  225. }
  226. }
  227. },
  228. {
  229. title: '上传时间',
  230. dataIndex: 'createTime',
  231. width: 180,
  232. render: ( text ) => {
  233. if ( text ) {
  234. return dayjs( text ).format( 'YYYY-MM-DD HH:mm:ss' );
  235. } else {
  236. return '--';
  237. }
  238. }
  239. },
  240. {
  241. title: '更新时间',
  242. dataIndex: 'updateTime',
  243. width: 180,
  244. render: ( text ) => {
  245. if ( text ) {
  246. return dayjs( text ).format( 'YYYY-MM-DD HH:mm:ss' );
  247. } else {
  248. return '--';
  249. }
  250. }
  251. },
  252. {
  253. title: '操作',
  254. dataIndex: 'operation',
  255. width: 80,
  256. fixed: 'right',
  257. render: ( text, record:any ) => {
  258. return (
  259. <>
  260. {
  261. <a
  262. style={ { marginRight: 16 } }
  263. onClick={ () => {
  264. // window.location= record.url
  265. window.open(`${record.url}`, '_blank');
  266. // window.open(`https://10.1.28.14:9000/deepseek-doc/%E6%95%B0%E5%AD%97%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0%E7%94%A8%E6%88%B7%E6%93%8D%E4%BD%9C%E6%89%8B%E5%86%8C_%E9%83%A8%E5%88%863.pdf`, '_blank');
  267. } }
  268. >
  269. 查看
  270. </a>
  271. }
  272. </>
  273. )
  274. }
  275. }
  276. ];
  277. const paginationConfig : TablePaginationConfig = {
  278. // 显示数据总量
  279. showTotal: ( total : number ) => {
  280. return `共 ${ total } 条`;
  281. },
  282. // 展示分页条数切换
  283. showSizeChanger: true,
  284. // 指定每页显示条数
  285. pageSizeOptions: [ '10', '20', '50', '100' ],
  286. // 快速跳转至某页
  287. showQuickJumper: true,
  288. current: page.page,
  289. pageSize: page.size,
  290. total: page.total,
  291. onChange: async ( page, pageSize ) => {
  292. await onChangePagination( page, pageSize );
  293. },
  294. };
  295. return (
  296. <div className='knowledgeLibInfo'>
  297. <Spin spinning={ uploadLoading || listLoading }>
  298. {
  299. <>
  300. <div className='knowledgeLibInfo-table'>
  301. <Table
  302. scroll={ { x: 'max-content' } }
  303. rowKey={ ( record ) => record.documentId }
  304. loading={ listLoading }
  305. columns={ columns }
  306. dataSource={ list }
  307. pagination={ paginationConfig }
  308. />
  309. </div>
  310. {
  311. infoModalOpen &&
  312. <InfoModal
  313. id={ infoModalId }
  314. open={ infoModalOpen }
  315. onClickConfirm={ infoModalOnClickConfirm }
  316. onClickCancel={ infoModalOnClickCancel }
  317. />
  318. }
  319. {
  320. infoModalSettingOpen &&
  321. <InfoModalSetting
  322. id={ infoModalSettingId }
  323. open={ infoModalSettingOpen }
  324. onClickConfirm={ infoModalSettingOnClickConfirm }
  325. onClickCancel={ infoModalSettingOnClickCancel }
  326. />
  327. }
  328. </>
  329. }
  330. </Spin>
  331. </div>
  332. );
  333. };
  334. export default observer( KnowledgeLibInfo );