index.tsx 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  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. Row,
  16. Col,
  17. Card
  18. } from 'antd';
  19. import { EditOutlined, DeleteOutlined, InboxOutlined, PlusOutlined, ArrowLeftOutlined, CloseOutlined, BulbOutlined, UpOutlined, DownOutlined,EyeOutlined } from '@ant-design/icons';
  20. import InfoModal from './components/InfoModal';
  21. import InfoModalSetting from './components/InfoModalSetting';
  22. import router from '@/router';
  23. import { Record } from './types';
  24. import dayjs from 'dayjs';
  25. import axios from 'axios';
  26. import LocalStorage from '@/LocalStorage';
  27. import store from './store';
  28. import './style.less';
  29. const { Dragger } = Upload;
  30. const KnowledgeLibInfo : React.FC = () => {
  31. const {
  32. state,
  33. init,
  34. sse,
  35. onClickModify,
  36. onClickDelete,
  37. onChangePagination,
  38. onClickDocumentDetail,
  39. infoModalOnClickConfirm,
  40. infoModalOnClickCancel,
  41. infoModalSettingOnClickConfirm,
  42. infoModalSettingOnClickCancel,
  43. onClickSettings,
  44. reset,
  45. onDeleteTakaiDocumentLibApi,
  46. } = store;
  47. const {
  48. knowledge_id,
  49. listLoading,
  50. page,
  51. list,
  52. processingList, // 处理中的状态
  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 params = useParams();
  62. const [ fileList, setFileList ] = React.useState<any[]>( [] );
  63. const [ uploading, setUploading ] = React.useState( false );
  64. const uploadMessageRef = React.useRef<(() => void) | null>( null );
  65. const [ sListFlag, setSListFlag ] = React.useState<boolean>();
  66. const [ cUpdateFlag, setCUpdateFlag ] = React.useState<boolean>();
  67. const [ detailFlag, setDetailFlag ] = React.useState<boolean>();
  68. const [ deleteFlag, setDeleteFlag ] = React.useState<boolean>();
  69. const [ createFlag, setCreateFlag ] = React.useState<boolean>();
  70. const [userInfoAll, setUserInfoAll] = React.useState<any>({});
  71. // 新手引导(知识库文档)整体可见性(持久化到 localStorage)
  72. const [showDocGuide, setShowDocGuide] = React.useState<boolean>(() => localStorage.getItem('knowledgeDocGuideHidden') !== 'true');
  73. // 新手引导展开/折叠状态(持久化到 localStorage),默认展开
  74. const [isDocGuideExpanded, setIsDocGuideExpanded] = React.useState<boolean>(() => {
  75. const saved = localStorage.getItem('knowledgeDocGuideExpanded');
  76. // 如果 localStorage 中没有值,默认展开(返回 true)
  77. return saved === null ? true : saved === 'true';
  78. });
  79. const toggleDocGuide = () => {
  80. setIsDocGuideExpanded(prev => {
  81. const newValue = !prev;
  82. // 保存到 localStorage
  83. localStorage.setItem('knowledgeDocGuideExpanded', String(newValue));
  84. return newValue;
  85. });
  86. };
  87. const props : UploadProps = {
  88. name: 'files',
  89. multiple: true,
  90. showUploadList:false,
  91. action: '/api/deepseek/api/uploadDocument/' + params.knowledgeId,
  92. headers: getHeaders(),
  93. beforeUpload( file, fileList ) {
  94. setUploadLoading( true );
  95. // const allowedExtensions = ['md', 'txt', 'pdf', 'jpg', 'png', 'jpeg', 'docx', 'xlsx', 'pptx', 'eml', 'csv', 'tar', 'gz', 'bz2', 'zip', 'rar', 'jar'];
  96. const allowedExtensions = [ 'txt', 'pdf', 'jpg', 'png', 'jpeg', 'doc', 'docx', 'ppt', 'pptx' ];
  97. // 检查文件类型
  98. for ( const file of fileList ) {
  99. const fileExt = file.name.split( '.' ).pop()?.toLowerCase();
  100. if ( !fileExt || !allowedExtensions.includes( fileExt ) ) {
  101. message.error( `不支持 ${ fileExt } 格式的文件上传` );
  102. setUploadLoading( false );
  103. return Upload.LIST_IGNORE;
  104. }
  105. }
  106. // 检查文件大小
  107. let totalSize = 0;
  108. for ( const file of fileList ) {
  109. const fileExt = file.name.split( '.' ).pop()?.toLowerCase();
  110. const fileSizeMB = file.size / 1024 / 1024;
  111. if ( fileSizeMB > 30 ) {
  112. message.error( '单个文件不能大于30M' );
  113. setUploadLoading( false );
  114. return Upload.LIST_IGNORE;
  115. }
  116. if ( [ 'jpg', 'png', 'jpeg' ].includes( fileExt! ) && fileSizeMB > 5 ) {
  117. message.error( '单张图片不能大于5M' );
  118. setUploadLoading( false );
  119. return Upload.LIST_IGNORE;
  120. }
  121. totalSize += fileSizeMB;
  122. }
  123. if ( totalSize > 125 ) {
  124. message.error( '文件总大小超过125M' );
  125. setUploadLoading( false );
  126. return Upload.LIST_IGNORE;
  127. }
  128. },
  129. onChange( info ) {
  130. const { status, name } = info.file;
  131. if ( status === 'uploading' ) {
  132. // 文件开始上传,显示持续提示
  133. if ( !uploadMessageRef.current ) {
  134. const closeMessage = message.loading(
  135. `文件 "${name}" 上传中...`,
  136. 0 // 0表示不自动关闭
  137. );
  138. uploadMessageRef.current = closeMessage;
  139. }
  140. setUploadLoading( true );
  141. } else if ( status === 'done' ) {
  142. console.log( status, 'status--done' );
  143. console.info( info.file.response, 'info.file.response.data' );
  144. // 关闭上传提示
  145. if ( uploadMessageRef.current ) {
  146. uploadMessageRef.current();
  147. uploadMessageRef.current = null;
  148. }
  149. if ( info.file.response.code === 200 && info.file.response.data === 1 ) {
  150. message.success( `${ info.file.name } 文件上传成功.` );
  151. init( params.knowledgeId );
  152. }else{
  153. message.error( info.file.response.msg || `${ info.file.name } 文件上传失败` );
  154. }
  155. setUploadLoading( false );
  156. } else if ( status === 'error' ) {
  157. console.log( status, 'status--error' );
  158. // 关闭上传提示
  159. if ( uploadMessageRef.current ) {
  160. uploadMessageRef.current();
  161. uploadMessageRef.current = null;
  162. }
  163. // 检查是否是504超时错误
  164. const response = info.file.response;
  165. const error = info.file.error;
  166. // 检查HTML格式的504错误或其他504错误格式
  167. const responseStr = String(response || '');
  168. const errorStr = String(error || '');
  169. if (responseStr.includes('504') ||
  170. responseStr.includes('Gateway Time-out') ||
  171. responseStr.includes('<h1>504 Gateway Time-out</h1>') ||
  172. errorStr.includes('timeout') ||
  173. errorStr.includes('504')) {
  174. info.file.response = '上传文件超时,请修改文件后再上传';
  175. info.file.error = '上传文件超时,请修改文件后再上传';
  176. message.error( '上传文件超时,请修改文件后再上传' );
  177. } else {
  178. message.error( `${ info.file.name } 文件上传失败` );
  179. }
  180. setUploadLoading( false );
  181. }
  182. },
  183. onDrop( e ) {
  184. console.log( 'Dropped files', e.dataTransfer.files );
  185. },
  186. };
  187. const handleUpload = async () => {
  188. if ( fileList.length === 0 ) return;
  189. setUploading( true );
  190. const formData = new FormData();
  191. // 添加所有文件
  192. fileList.forEach( file => {
  193. if ( file.originFileObj ) {
  194. formData.append( 'files', file.originFileObj );
  195. }
  196. } );
  197. try {
  198. const res = await axios.post( '/api/deepseek/api/uploadDocument/' + params.knowledgeId, formData, {
  199. headers: { 'Content-Type': 'multipart/form-data' }
  200. } );
  201. message.success( `${ fileList.length }个文件上传成功` );
  202. setFileList( [] );
  203. } catch ( err: any ) {
  204. // 检查是否是504超时错误
  205. if (err?.response?.status === 504 || err?.code === 'ECONNABORTED' || String(err).includes('timeout')) {
  206. message.error( '上传文件超时,请修改文件后再上传' );
  207. } else {
  208. message.error( '上传失败' );
  209. }
  210. } finally {
  211. setUploading( false );
  212. }
  213. };
  214. React.useEffect( () => {
  215. init( params.knowledgeId );
  216. const cList = LocalStorage.getStatusFlag( 'deepseek:slice:list' );
  217. setSListFlag( cList );
  218. const cDetail = LocalStorage.getStatusFlag( 'deepseek:config:update' );
  219. setCUpdateFlag( cDetail );
  220. const detail = LocalStorage.getStatusFlag( 'deepseek:document:detail' );
  221. setDetailFlag( detail );
  222. const deleteF = LocalStorage.getStatusFlag( 'deepseek:document:delete' );
  223. setDeleteFlag( deleteF );
  224. const createF = LocalStorage.getStatusFlag( 'deepseek:document:create' );
  225. setCreateFlag( createF );
  226. setUserInfoAll(LocalStorage.getUserInfo());
  227. sse()
  228. return () => reset();
  229. }, [] );
  230. const columns : TableColumnsType<Record> = [
  231. {
  232. title: '序号',
  233. dataIndex: 'index',
  234. width: 80,
  235. render: ( text, record, index ) => {
  236. return index + 1;
  237. }
  238. },
  239. {
  240. title: '文件名',
  241. dataIndex: 'name',
  242. width: 300,
  243. sorter: (a, b) => a.name.localeCompare(b.name),
  244. render: ( text, record ) => {
  245. return (
  246. `${ text }`
  247. )
  248. }
  249. },
  250. {
  251. title: '文件大小',
  252. dataIndex: 'length',
  253. width: 100,
  254. render: ( text ) => {
  255. if ( text ) {
  256. const size = ( text / 1024 / 1024 ).toFixed( 2 );
  257. return `${ size } M`;
  258. } else {
  259. return '--'
  260. }
  261. }
  262. },
  263. {
  264. title: '字符数量',
  265. dataIndex: 'wordNum',
  266. width: 100,
  267. render: ( text ) => {
  268. if ( text ) {
  269. return `${ text }`;
  270. } else {
  271. return '--'
  272. }
  273. }
  274. },
  275. {
  276. title: '分段',
  277. dataIndex: 'sliceTotal',
  278. width: 100,
  279. render: ( text ) => {
  280. if ( text ) {
  281. return `${ text }`;
  282. } else {
  283. return '--';
  284. }
  285. }
  286. },
  287. {
  288. title: '上传时间',
  289. dataIndex: 'createTime',
  290. width: 180,
  291. render: ( text ) => {
  292. if ( text ) {
  293. return dayjs( text ).format( 'YYYY-MM-DD HH:mm:ss' );
  294. } else {
  295. return '--';
  296. }
  297. }
  298. },
  299. {
  300. title: '更新时间',
  301. dataIndex: 'updateTime',
  302. width: 180,
  303. render: ( text ) => {
  304. if ( text ) {
  305. return dayjs( text ).format( 'YYYY-MM-DD HH:mm:ss' );
  306. } else {
  307. return '--';
  308. }
  309. }
  310. },
  311. {
  312. title: '操作',
  313. dataIndex: 'operation',
  314. width: 180,
  315. fixed: 'right',
  316. render: ( text, record:any ) => {
  317. return (
  318. <>
  319. { (createFlag||userInfoAll.id===record.createBy) &&
  320. <a
  321. style={ { marginRight: 16 } }
  322. onClick={ () => {
  323. const path = generatePath( '/deepseek/knowledgeLib/:knowledgeId/:createBy/slice/:documentId/:embeddingId', {
  324. knowledgeId: params.knowledgeId as string,
  325. createBy: params.createBy as string,
  326. documentId: record.documentId,
  327. embeddingId: state.knowledgeDetail.embeddingId,
  328. } );
  329. router.navigate( { pathname: path },{state:{ createBy:record.createBy}} );
  330. } }
  331. >
  332. 切片
  333. </a>
  334. }
  335. {
  336. (cUpdateFlag||userInfoAll.id===record.createBy) &&
  337. <a
  338. style={ { marginRight: 16 } }
  339. onClick={ () => {
  340. onClickSettings( record.documentId );
  341. } }
  342. >
  343. 配置
  344. </a>
  345. }
  346. {
  347. (cUpdateFlag||userInfoAll.id===record.createBy) &&
  348. <a
  349. style={ { marginRight: 16 } }
  350. onClick={ () => {
  351. onClickModify( record.documentId );
  352. } }>
  353. <EditOutlined />
  354. </a>
  355. }
  356. {
  357. (deleteFlag||userInfoAll.id===record.createBy) &&
  358. <a
  359. className='text-error'
  360. onClick={ () => {
  361. Modal.confirm( {
  362. title: '删除',
  363. content: `确定删除知识文件:${ record.name }吗?`,
  364. okType: 'danger',
  365. onOk: async () => {
  366. const userInfo = LocalStorage.getUserInfo();
  367. await onClickDelete( record.documentId );
  368. }
  369. } );
  370. } }
  371. >
  372. <DeleteOutlined />
  373. </a>
  374. }
  375. </>
  376. )
  377. }
  378. }
  379. ];
  380. const paginationConfig : TablePaginationConfig = {
  381. // 显示数据总量
  382. showTotal: ( total : number ) => {
  383. return `共 ${ total } 条`;
  384. },
  385. // 展示分页条数切换
  386. showSizeChanger: true,
  387. // 指定每页显示条数
  388. pageSizeOptions: [ '10', '20', '50', '100' ],
  389. // 快速跳转至某页
  390. showQuickJumper: true,
  391. current: page.page,
  392. pageSize: page.size,
  393. total: page.total,
  394. onChange: async ( page, pageSize ) => {
  395. await onChangePagination( page, pageSize );
  396. },
  397. };
  398. const progressBar = () => {
  399. return processingList.map((item: any, index: number) => {
  400. return <div key={index} className='knowledgeLibInfo-progress'>
  401. <div className="task-card bg-[#FAFAFA] rounded-xl py-2 px-5 card-shadow task-item fade-in" data-status="processing" data-name="市场调研报告需求.docx" data-date="2023-06-15 09:32" data-project-id="1" data-project-name="电商平台重构" data-project-color="#165DFF">
  402. <div className="flex flex-wrap justify-between items-start mb-2">
  403. <div>
  404. <div className="flex items-center">
  405. <p className="font-semibold text-[14px]" style={{ marginRight: 16 }}>{item?.name}</p>
  406. </div>
  407. <p className="text-[12px] text-gray-500">上传于:{item?.createTime}</p>
  408. </div>
  409. {item?.status === '0' && <span className="bg-blue-100 text-primary text-xs font-medium px-2.5 py-0.5 rounded-full flex items-center mt-1">
  410. <i className="fa fa-spinner fa-spin mr-1"></i> 处理中
  411. </span>}
  412. {item?.status === '2' && <span className="bg-blue-100 text-primary text-xs font-medium px-2.5 py-0.5 rounded-full flex items-center mt-1">
  413. 处理失败
  414. </span>}
  415. {item?.status === '3' && <span className="bg-blue-100 text-primary text-xs font-medium px-2.5 py-0.5 rounded-full flex items-center mt-1">
  416. 待处理
  417. </span>}
  418. {item?.progress === 100 && <span className="bg-green-100 text-success text-xs font-medium px-2.5 py-0.5 rounded-full flex items-center mt-1">
  419. <i className="fa fa-check mr-1"></i> 已完成
  420. </span>}
  421. </div>
  422. <div className="mb-1">
  423. <div className="flex justify-between text-[12px] mb-1">
  424. <span>处理进度</span>
  425. <span id="progressText-1">{item?.progress}%</span>
  426. </div>
  427. <div className="w-full bg-gray-200 rounded-full h-2.5">
  428. <div className="progress-bar bg-blue-500 h-2.5 rounded-full" style={{ width: `${item?.progress}%` }} id="progressBar-1"></div>
  429. </div>
  430. <div className="flex justify-between text-sm mt-1">
  431. <span></span>
  432. <p>
  433. <EyeOutlined className='mr-2 cursor-pointer' onClick={() => {
  434. window.open(item?.url)
  435. }} />
  436. <DeleteOutlined className='text-error cursor-pointer' onClick={() => {
  437. Modal.confirm({
  438. title: '删除',
  439. content: `确定删除知识文件:${item.name}吗?`,
  440. okType: 'danger',
  441. onOk: async () => {
  442. onDeleteTakaiDocumentLibApi(item.documentId)
  443. }
  444. });
  445. }} />
  446. </p>
  447. </div>
  448. </div>
  449. </div>
  450. </div>
  451. })
  452. }
  453. return (
  454. <div className='knowledgeLibInfo'>
  455. <Spin spinning={ uploadLoading || listLoading }>
  456. {showDocGuide && (
  457. <div style={{ padding: '12px 20px 12px 20px', background: '#fff' }}>
  458. <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: isDocGuideExpanded ? 8 : 0 }}>
  459. <div style={{ fontSize: 13, color: '#333', display: 'flex', alignItems: 'center', gap: 6 }}>
  460. <BulbOutlined style={{ color: '#faad14' }} />
  461. 提示:如何上传并管理知识库文档?
  462. </div>
  463. <div onClick={toggleDocGuide} style={{ color: '#999', cursor: 'pointer', display: 'flex', alignItems: 'center' }}>
  464. {isDocGuideExpanded ? <UpOutlined /> : <DownOutlined />}
  465. </div>
  466. </div>
  467. {isDocGuideExpanded && (
  468. <Row gutter={12}>
  469. <Col xs={24} sm={24} md={12} lg={8}>
  470. <Card
  471. size="small"
  472. bordered={false}
  473. style={{
  474. background: 'linear-gradient(90deg, #e6f4ff 0%, #f0f7ff 100%)',
  475. boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
  476. borderRadius: 8,
  477. height: '100%'
  478. }}
  479. bodyStyle={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', minHeight: 80 }}
  480. title={<span style={{ fontWeight: 600 }}>上传知识文档</span>}
  481. >
  482. <div style={{ color: '#666', fontSize: 12 }}>支持多种格式(Word、PDF、图片等),单个/总大小有上限,上传后自动处理(约5-10分钟,如遇到超时,可等待几分钟后刷新页面,将自动成功)。</div>
  483. {/* <div style={{ color: '#666', fontSize: 12 }}>支持pdf,doc,docx文件格式上传,建议单个文件不要超过10M,页数控制在100页以内。尽量单文件上传。</div> */}
  484. <div style={{ textAlign: 'right', color: '#1677ff', fontWeight: 600 }}>step 1</div>
  485. </Card>
  486. </Col>
  487. <Col xs={24} sm={24} md={12} lg={8}>
  488. <Card
  489. size="small"
  490. bordered={false}
  491. style={{
  492. background: 'linear-gradient(90deg, #e6fffb 0%, #f0fffe 100%)',
  493. boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
  494. borderRadius: 8,
  495. height: '100%'
  496. }}
  497. bodyStyle={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', minHeight: 80 }}
  498. title={<span style={{ fontWeight: 600 }}>查看切片与索引</span>}
  499. >
  500. <div style={{ color: '#666', fontSize: 12 }}>自动解析成功后可查看切片数量与索引状态,必要时可重新生成或调整配置。</div>
  501. <div style={{ textAlign: 'right', color: '#1677ff', fontWeight: 600 }}>step 2</div>
  502. </Card>
  503. </Col>
  504. <Col xs={24} sm={24} md={12} lg={8}>
  505. <Card
  506. size="small"
  507. bordered={false}
  508. style={{
  509. background: 'linear-gradient(90deg, #fff7e6 0%, #fffaf0 100%)',
  510. boxShadow: '0 1px 2px rgba(0,0,0,0.04)',
  511. borderRadius: 8,
  512. height: '100%'
  513. }}
  514. bodyStyle={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', minHeight: 80 }}
  515. title={<span style={{ fontWeight: 600 }}>在应用中生效</span>}
  516. >
  517. <div style={{ color: '#666', fontSize: 12 }}>在RAG应用中切片将会被模型检索并召回,形成大模型对话的依据语料。</div>
  518. <div style={{ textAlign: 'right', color: '#1677ff', fontWeight: 600 }}>step 3</div>
  519. </Card>
  520. </Col>
  521. </Row>
  522. )}
  523. </div>
  524. )}
  525. {progressBar()}
  526. {
  527. page.total === 0 &&
  528. <div className='knowledgeLibInfo-operation'>
  529. <div>
  530. <Button
  531. type='primary'
  532. icon={ <ArrowLeftOutlined /> }
  533. onClick={ () => {
  534. router.navigate( { pathname: '/deepseek/knowledgeLib' } );
  535. } }
  536. >
  537. 返回
  538. </Button>
  539. </div>
  540. {
  541. ((createFlag&&userInfoAll.id==='1')||userInfoAll.id===params?.createBy) &&
  542. <div style={ { marginTop: 20, width: '100%', height: '200px' } }>
  543. <Dragger { ...props }>
  544. <p className="ant-upload-drag-icon">
  545. <InboxOutlined />
  546. </p>
  547. <p>
  548. 点击上传,或拖放文件到此处
  549. </p>
  550. <p className="ant-upload-hint">
  551. {/* 支持文件格式md,txt,pdf,jpg,png,jpeg,docx,xlsx,
  552. pptx,eml,csv,单个文档 ≤ 30M,单张图片 ≤ 2.5M,文件总
  553. 大小不得超过125M. */}
  554. 支持pdf,doc,docx,jpg,png,jpeg文件格式上传,建议单个文件不要超过10M,页数控制在100页以内。尽量单文件上传。
  555. </p>
  556. </Dragger>
  557. </div>
  558. }
  559. </div>
  560. }
  561. {
  562. page.total > 0 &&
  563. <>
  564. <div className='knowledgeLibInfo-operation'>
  565. <Button
  566. style={ { marginRight: 16 } }
  567. type='primary'
  568. icon={ <ArrowLeftOutlined /> } onClick={ () => {
  569. router.navigate( { pathname: '/deepseek/knowledgeLib' } );
  570. } }>
  571. 返回
  572. </Button>
  573. {
  574. ((createFlag&&userInfoAll.id==='1')||userInfoAll.id===params?.createBy) &&
  575. <Upload { ...props }>
  576. <Button type='primary' icon={ <PlusOutlined /> }>上传知识文件</Button>
  577. </Upload>
  578. }
  579. </div>
  580. <div className='knowledgeLibInfo-table'>
  581. <Table
  582. scroll={ { x: 'max-content' } }
  583. rowKey={ ( record ) => record.documentId }
  584. loading={ listLoading }
  585. columns={ columns }
  586. dataSource={ list }
  587. pagination={ paginationConfig }
  588. />
  589. </div>
  590. {
  591. infoModalOpen &&
  592. <InfoModal
  593. id={ infoModalId }
  594. open={ infoModalOpen }
  595. onClickConfirm={ infoModalOnClickConfirm }
  596. onClickCancel={ infoModalOnClickCancel }
  597. />
  598. }
  599. {
  600. infoModalSettingOpen &&
  601. <InfoModalSetting
  602. id={ infoModalSettingId }
  603. open={ infoModalSettingOpen }
  604. onClickConfirm={ infoModalSettingOnClickConfirm }
  605. onClickCancel={ infoModalSettingOnClickCancel }
  606. />
  607. }
  608. </>
  609. }
  610. </Spin>
  611. </div>
  612. );
  613. };
  614. export default observer( KnowledgeLibInfo );