index.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. import React, { useState, useEffect } from 'react';
  2. import {
  3. Input,
  4. Tree,
  5. Table,
  6. Button,
  7. Form,
  8. DatePicker,
  9. Divider,
  10. Modal,
  11. Checkbox,
  12. TreeSelect,
  13. Upload,
  14. Space,
  15. message,
  16. Spin,
  17. } from 'antd';
  18. import dayjs from 'dayjs';
  19. import { PageContainer, ProCard } from '@ant-design/pro-components';
  20. import { useRequest, useModel } from '@umijs/max';
  21. import {
  22. queryDirCreate,
  23. queryDirList,
  24. queryFileDelete,
  25. queryFileList,
  26. queryFileUpload,
  27. queryOAFile,
  28. queryPermitList,
  29. querySetPermit,
  30. } from '../../services/file';
  31. import { queryGetContractList } from '../../services/contract';
  32. import { downloadFile, getToken } from '@/utils/utils';
  33. import {
  34. ExclamationCircleOutlined,
  35. PlusCircleOutlined,
  36. } from '@ant-design/icons';
  37. import AddFileModal from './components/model';
  38. import PerModal from './components/PreModal';
  39. import { queryAuditList, createAduit } from '@/services/boom';
  40. import { stringify } from 'qs';
  41. import FileViewerModal from '@/components/FileViewerNew';
  42. const tempData = [
  43. { name: '文件1', upload_user: '管理员', upload_time: '2023-04-08 11:00:00' },
  44. { name: '文件2', upload_user: '管理员', upload_time: '2023-04-10 11:00:00' },
  45. ];
  46. const { DirectoryTree } = Tree;
  47. const { Search } = Input;
  48. const { RangePicker } = DatePicker;
  49. function FileManagement(props) {
  50. const [form] = Form.useForm();
  51. const [modal, contextHolder] = Modal.useModal();
  52. const { user } = useModel('userInfo');
  53. const [tableData, setTableData] = useState([]);
  54. const [visible, setVisible] = useState(false);
  55. const [node, setNode] = useState();
  56. const [selectedRowKeys, setSelectedRowKeys] = useState([]);
  57. const [fileViewerVisible, setFileViewerVisible] = useState(false);
  58. const [fileViewerData, setFileViewerData] = useState();
  59. const {
  60. data: treeData,
  61. loading: treeLoading,
  62. run: runFileDir,
  63. } = useRequest((data) => queryDirList(data), {
  64. formatResult: (res) => {
  65. const result = [];
  66. result[0] = res?.data.limit_list[0];
  67. result[1] = res?.data.list[0];
  68. return result;
  69. },
  70. });
  71. const { loading, run } = useRequest((data) => queryFileList(data), {
  72. manual: true,
  73. onSuccess: (data) => {
  74. let result =
  75. data?.list?.map((item, idx) => {
  76. return {
  77. ...item,
  78. dir_name: item.file_name,
  79. create_time: item.created_on,
  80. key: idx,
  81. };
  82. }) || [];
  83. setTableData(result);
  84. setSelectedRowKeys([]);
  85. },
  86. });
  87. const { loading: OAloading, run: runOA } = useRequest(
  88. (data) => queryOAFile(data),
  89. {
  90. manual: true,
  91. onSuccess: (data) => {
  92. let result =
  93. data?.list.map((item, idx) => {
  94. let name;
  95. if (item?.name) {
  96. name = item.name;
  97. } else if (item?.path) {
  98. const list = item.path.split('/');
  99. name = list?.length > 0 ? list[list.length - 1] : '-';
  100. } else if (item?.url) {
  101. const list = item?.url?.split('/');
  102. name = list?.length > 0 ? list[list.length - 1] : '-';
  103. }
  104. return {
  105. ...item,
  106. dir_name: name,
  107. create_time: dayjs(item.c_time).format('YYYY-MM-DD'),
  108. key: idx,
  109. };
  110. }) || [];
  111. setTableData(result);
  112. setSelectedRowKeys([]);
  113. },
  114. },
  115. );
  116. const { loading: contractLoading, run: runContract } = useRequest(
  117. (data) => queryGetContractList({ ...data, status: 3 }),
  118. {
  119. manual: true,
  120. onSuccess: (data) => {
  121. let result =
  122. data?.list.map((item, idx) => {
  123. return {
  124. ...item,
  125. dir_name: item.name,
  126. create_time: item.created_on,
  127. key: idx,
  128. };
  129. }) || [];
  130. setTableData(result);
  131. setSelectedRowKeys([]);
  132. },
  133. },
  134. );
  135. const { loading: createLoading, run: RunCreate } = useRequest(
  136. (data) => queryDirCreate(data),
  137. {
  138. manual: true,
  139. onSuccess: () => {
  140. setVisible(false);
  141. runFileDir();
  142. message.success('创建成功');
  143. },
  144. onError: () => {
  145. message.success('创建失败');
  146. },
  147. },
  148. );
  149. //申请权限
  150. const { loading: perLoading, run: runPer } = useRequest(
  151. (data) => querySetPermit(data),
  152. {
  153. manual: true,
  154. onSuccess: (data) => {
  155. message.success('申请成功');
  156. },
  157. onError: () => {
  158. message.error('申请失败');
  159. },
  160. },
  161. );
  162. //上传文件
  163. const { run: runUploadFiles } = useRequest((data) => queryFileUpload(data), {
  164. manual: true,
  165. onSuccess: (data) => {
  166. updateTableFile(node);
  167. message.success('文件上传成功');
  168. },
  169. onError: () => {
  170. message.error('文件上传失败');
  171. },
  172. });
  173. //文件审批列表
  174. const { data: auditList } = useRequest(() =>
  175. queryAuditList({ flow_type: 1 }),
  176. );
  177. //发起申请权限的文件审批
  178. const { loading: createLoadin, run: runAuditCreate } = useRequest(
  179. createAduit,
  180. {
  181. manual: true,
  182. onSuccess() {
  183. message.success('申请审批成功');
  184. setPerOpen(false);
  185. },
  186. },
  187. );
  188. //文档权限列表
  189. const {
  190. data: preListData,
  191. loading: preListLoading,
  192. run: runPreList,
  193. } = useRequest((data) => queryPermitList(data), {
  194. manual: true,
  195. });
  196. //删除文件
  197. const { loading: delFileLoading, run: runDelFile } = useRequest(
  198. (data) => queryFileDelete(data),
  199. {
  200. manual: true,
  201. onSuccess: () => {
  202. updateTableFile(node);
  203. message.success('删除成功');
  204. },
  205. onError: () => {
  206. message.success('删除失败');
  207. },
  208. },
  209. );
  210. const [expandedKeys, setExpandedKeys] = useState([]);
  211. const [searchValue, setSearchValue] = useState('');
  212. const [permissionOpen, setPerOpen] = useState(false);
  213. const [addOpen, setAddOpen] = useState(false);
  214. const delConfirm = (record) => {
  215. modal.confirm({
  216. title: '删除',
  217. icon: <ExclamationCircleOutlined />,
  218. content: `确定删除文件${record.dir_name}, 删除后不能恢复`,
  219. okText: '确认',
  220. cancelText: '取消',
  221. onOk: () => runDelFile({ file_id: record.id }),
  222. });
  223. };
  224. const isShow = (id, num) => {
  225. //num 下载 1 删除 2
  226. let bool = false;
  227. const idx = preListData?.list?.findIndex(
  228. (item) => item.file_id == id && item.permit == num,
  229. );
  230. if (idx > -1) bool = true;
  231. return bool;
  232. };
  233. const columns = [
  234. { title: '文档名称', dataIndex: 'dir_name' },
  235. { title: '上传人员', align: 'center', dataIndex: 'user_name' },
  236. {
  237. title: '上传时间',
  238. align: 'center',
  239. dataIndex: 'create_time',
  240. render: (text) => dayjs(text).format('YYYY-MM-DD'),
  241. },
  242. {
  243. title: '操作',
  244. render: (_, record) => (
  245. <Space>
  246. <a onClick={() => handleSeeClick(record)}>查看</a>
  247. {isShow(record.id, 1) && (
  248. <a onClick={() => onDownload(record)}>下载</a>
  249. )}
  250. {isShow(record.id, 2) && node?.dir_type == 0 && (
  251. <a onClick={() => delConfirm(record)}>删除</a>
  252. )}
  253. </Space>
  254. ),
  255. },
  256. ];
  257. useEffect(() => {
  258. //获取列表的权限
  259. if (!tableData || tableData.length <= 0) return;
  260. const ids = tableData.map((item) => item.id);
  261. const data = {
  262. file_type: node.dir_type,
  263. file_ids: ids?.join(','),
  264. };
  265. runPreList(data);
  266. }, [tableData]);
  267. // 搜索文件夹树
  268. const onSearchDirectory = (value, nodes = treeData) => {
  269. const expandedKeys = getExpandedKeys(nodes, value);
  270. setExpandedKeys(expandedKeys);
  271. setSearchValue(value);
  272. };
  273. // 根据搜索值(value)获取key列表
  274. const getExpandedKeys = (nodes, value) => {
  275. if (!value) return [];
  276. let result = [];
  277. nodes.forEach((node) => {
  278. // 若该节点名称包含搜索值,将key加入result
  279. if (node.dir_name.includes(value)) {
  280. result.push(node.id);
  281. }
  282. // 若该节点的子节点包含搜索值,将子节点key和该节点key加入result
  283. if (node.children) {
  284. let getChildren = getExpandedKeys(node.children, value);
  285. if (getChildren.length != 0)
  286. result = [...result, node.id, ...getChildren];
  287. }
  288. });
  289. return result;
  290. };
  291. const onExpand = (expandedKeys) => {
  292. setExpandedKeys(expandedKeys);
  293. };
  294. const filterTreeNode = (node) =>
  295. searchValue.length > 0 ? node.dir_name.includes(searchValue) : false;
  296. // 搜索文件
  297. const onSearch = () => {
  298. form
  299. .validateFields()
  300. .then((values) => {
  301. console.log(values);
  302. })
  303. .catch((err) => {
  304. return;
  305. });
  306. };
  307. const findListById = (id) => {
  308. if (!id) return;
  309. const fun = (data) => {
  310. for (let i = 0; i < data.length; i++) {
  311. let item = data[i];
  312. if (item.id == id) {
  313. return item.children;
  314. } else if (item.children) {
  315. let res = fun(item.children);
  316. if (res) return res;
  317. }
  318. }
  319. };
  320. const list = fun(treeData);
  321. return list?.map((item, idx) => {
  322. return { ...item, key: idx };
  323. });
  324. };
  325. const updateTableFile = (node) => {
  326. if (node.id == 1) {
  327. //点击受控文件直接把文件夹下的文件夹列表显示出来
  328. setTableData(findListById(1));
  329. setSelectedRowKeys([]);
  330. } else if (node.id == 3) {
  331. //点击合同文件直接把文件夹下的文件夹列表显示出来
  332. setTableData(findListById(3));
  333. setSelectedRowKeys([]);
  334. } else if (node.id == 7) {
  335. //合同归档走合同接口
  336. runContract({});
  337. } else if (node.is_limit) {
  338. //其他受控文件走classify_id
  339. runOA({ classify_id: node.classify_id });
  340. } else {
  341. //部门文件
  342. run({ dir_id: node.id });
  343. }
  344. };
  345. const handleSelect = (SelectKeys, e) => {
  346. // console.log(e, SelectKeys);
  347. const node = e.node;
  348. setNode(node);
  349. updateTableFile(e.node);
  350. };
  351. const onDownload = (record) => {
  352. const data = {
  353. file_id: record.id,
  354. path: record.path,
  355. file_type: node.dir_type,
  356. };
  357. window.downloadFile(
  358. `/api/archive/v1/file/download?${stringify(data)}`,
  359. record.dir_name,
  360. false,
  361. );
  362. };
  363. const handleSeeClick = (record) => {
  364. if (node?.dir_type == 2) {
  365. // 合同归档
  366. if (!record.attach) return;
  367. const attach = JSON.parse(record.attach);
  368. setFileViewerData(attach);
  369. setFileViewerVisible(true);
  370. } else {
  371. const token = getToken();
  372. const params = {
  373. file_id: record.id,
  374. path: record.path,
  375. file_type: node.dir_type,
  376. 'JWT-TOKEN': token,
  377. };
  378. const url = `${location.origin}/api/archive/v1/file/download?${stringify(
  379. params,
  380. )}`;
  381. const arr = record.dir_name.split('.');
  382. const type = arr[arr.length - 1];
  383. const data = { url, name: record.dir_name, type };
  384. setFileViewerData(data);
  385. setFileViewerVisible(true);
  386. }
  387. };
  388. const handleFilesChange = () => {
  389. const inputDom = document.getElementById('files');
  390. let formData = new FormData();
  391. formData.append('dir_id', node.id);
  392. formData.append('user_name', user.CName);
  393. if (inputDom.files?.length > 0) {
  394. for (let i = 0; i < inputDom.files.length; i++) {
  395. formData.append('files', inputDom.files[i]);
  396. }
  397. runUploadFiles(formData);
  398. }
  399. };
  400. return (
  401. <PageContainer>
  402. <div style={{ display: 'flex', justifyContent: 'space-between' }}>
  403. <ProCard style={{ height: '100%', width: '30%' }}>
  404. <Search onChange={(e) => onSearchDirectory(e.target.value)} />
  405. <Spin spinning={treeLoading}>
  406. <DirectoryTree
  407. expandedKeys={expandedKeys}
  408. onExpand={onExpand}
  409. treeData={treeData}
  410. onSelect={handleSelect}
  411. fieldNames={{
  412. key: 'id',
  413. title: 'dir_name',
  414. children: 'children',
  415. }}
  416. filterTreeNode={filterTreeNode}
  417. titleRender={(item) => {
  418. return item.dir_name == '部门文件' ? (
  419. <Space>
  420. <span>{item.dir_name}</span>
  421. <PlusCircleOutlined
  422. style={{ fontSize: '16px' }}
  423. onClick={() => {
  424. setNode(item);
  425. setVisible(true);
  426. }}
  427. />
  428. </Space>
  429. ) : (
  430. <span>{item.dir_name}</span>
  431. );
  432. }}
  433. />
  434. </Spin>
  435. </ProCard>
  436. <ProCard style={{ height: '100%', width: 'calc(70% - 20px)' }}>
  437. <Form layout="inline" form={form}>
  438. <Form.Item name="date">
  439. <RangePicker />
  440. </Form.Item>
  441. <Form.Item name="name">
  442. <Input />
  443. </Form.Item>
  444. <Form.Item>
  445. <Button type="primary" onClick={onSearch}>
  446. 查询
  447. </Button>
  448. </Form.Item>
  449. <Form.Item>
  450. <Button
  451. type="primary"
  452. onClick={() => document.getElementById('files')?.click()}
  453. disabled={node ? false : true}
  454. >
  455. 上传
  456. </Button>
  457. </Form.Item>
  458. <Form.Item>
  459. <Button
  460. type="primary"
  461. onClick={() => setPerOpen(true)}
  462. disabled={selectedRowKeys?.length > 0 ? false : true}
  463. >
  464. 申请权限
  465. </Button>
  466. </Form.Item>
  467. </Form>
  468. <div>
  469. <Table
  470. columns={columns}
  471. dataSource={tableData}
  472. rowSelection={{
  473. selectedRowKeys,
  474. type: 'radio',
  475. onChange: (e, fileNode) => {
  476. console.log(e);
  477. setSelectedRowKeys(e);
  478. // (fileNode[0])
  479. },
  480. }}
  481. loading={OAloading || contractLoading || loading}
  482. style={{ overflowY: 'auto' }}
  483. childrenColumnName="none"
  484. pagination={false}
  485. />
  486. {/*
  487. <Button type="primary" onClick={() => setAddOpen(true)}>
  488. <PlusOutlined />
  489. 新增权限
  490. </Button>
  491. {!editPer && (
  492. <Button
  493. type="primary"
  494. onClick={() => setEditPer(true)}
  495. style={{ marginLeft: 20 }}
  496. >
  497. 编辑权限
  498. </Button>
  499. )}
  500. {editPer && (
  501. <Button
  502. type="primary"
  503. onClick={() => setEditPer(false)}
  504. style={{ marginLeft: 20 }}
  505. >
  506. 确定
  507. </Button>
  508. )}
  509. <Table
  510. columns={columnsPer}
  511. dataSource={tempPer}
  512. style={{ overflowY: 'auto' }}
  513. />
  514. */}
  515. </div>
  516. </ProCard>
  517. <Input
  518. id="files"
  519. type="file"
  520. style={{ display: 'none' }}
  521. onChange={handleFilesChange}
  522. multiple
  523. />
  524. {contextHolder}
  525. </div>
  526. {/* <PerModal /> */}
  527. <AddModal />
  528. <AddFileModal
  529. id={node?.id}
  530. visible={visible}
  531. handleOk={(value) => {
  532. RunCreate({ ...value, user_name: user?.CName });
  533. }}
  534. handleCancel={() => setVisible(false)}
  535. />
  536. <PerModal
  537. node={node}
  538. fileNode={tableData?.find((item) => item.key == selectedRowKeys[0])}
  539. auditList={auditList}
  540. visible={permissionOpen}
  541. handleCancel={() => setPerOpen(false)}
  542. handleOk={(data) => {
  543. runAuditCreate(data);
  544. }}
  545. />
  546. <FileViewerModal
  547. data={fileViewerData}
  548. visible={fileViewerVisible}
  549. downloadFile={() => {}}
  550. onCancel={() => {
  551. setFileViewerVisible(false);
  552. }}
  553. />
  554. </PageContainer>
  555. );
  556. function AddModal(props) {
  557. const perList = [
  558. { label: '查看列表', value: 'a', disabled: true },
  559. { label: '只读', value: 'b', disabled: true },
  560. { label: '下载', value: 'c' },
  561. { label: '删除', value: 'd' },
  562. { label: '授权', value: 'e' },
  563. ];
  564. const rowSelection = {
  565. onChange: (selectedRowKeys, selectedRows) => {
  566. console.log(
  567. `selectedRowKeys: ${selectedRowKeys}`,
  568. 'selectedRows: ',
  569. selectedRows,
  570. );
  571. },
  572. };
  573. return (
  574. <Modal
  575. title="新增权限"
  576. open={addOpen}
  577. onCancel={() => setAddOpen(false)}
  578. width={800}
  579. >
  580. <Table
  581. title={() => '文档列表'}
  582. columns={columns.slice(0, -1)}
  583. dataSource={tempData}
  584. pagination={false}
  585. rowSelection={rowSelection}
  586. destroyOnClose
  587. />
  588. <div style={{ margin: '20px 0px' }}>
  589. <span style={{ marginRight: 20 }}>选择用户:</span>
  590. <TreeSelect multiple={true} style={{ width: 200 }}></TreeSelect>
  591. </div>
  592. <div>
  593. <span style={{ marginRight: 20 }}>选择权限:</span>
  594. <Checkbox.Group options={perList} defaultValue={['a', 'b']} />
  595. </div>
  596. </Modal>
  597. );
  598. }
  599. }
  600. export default FileManagement;