drawerIndex.tsx 10 KB

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