index.tsx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /*
  2. * @Author: code4eat awesomedema@gmail.com
  3. * @Date: 2022-12-16 09:42:52
  4. * @LastEditors: code4eat awesomedema@gmail.com
  5. * @LastEditTime: 2023-05-30 11:01:02
  6. * @FilePath: /BudgetManaSystem/src/pages/budgetMana/monthlySet/index.tsx
  7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  8. */
  9. import BMSPagecontainer from '@/components/BMSPageContainer';
  10. import { useEffect, useImperativeHandle, useRef, useState } from 'react';
  11. import './style.less';
  12. import { TreeProps, Input, Modal, Transfer, Popconfirm, Table, message } from 'antd';
  13. import { DataNode } from 'antd/es/tree';
  14. import expandedIcon from '../../../../../static/treenode_open.png';
  15. import closeIcon from '../../../../../static/treenode_collapse.png';
  16. import { BMSTable } from '@/components/BMSTable';
  17. import { ActionType, ProColumns } from '@ant-design/pro-components';
  18. import { createFromIconfontCN } from '@ant-design/icons';
  19. import { CheckUnitDepMapTableDataType, delTableData, addMapDep, getCheckUnitDepMapTableDataByUnitClass, getMapDepList, getTreeData, getTreeDataRespType } from './service';
  20. import { TransferItem, TransferProps } from 'antd/es/transfer';
  21. import difference from 'lodash/difference';
  22. import 'dayjs/locale/zh-cn';
  23. import React from 'react';
  24. import DirectoryTree from 'antd/es/tree/DirectoryTree';
  25. import { getDeepestTreeData } from '@/utils/tooljs';
  26. import { ColumnsType, TableRowSelection } from 'antd/es/table/interface';
  27. import '../../../../utils/zhongtaiB';
  28. import '../../../../utils/zhongtaiC';
  29. const IconFont = createFromIconfontCN({
  30. scriptUrl: '',
  31. });
  32. const SearchIcon = createFromIconfontCN({
  33. scriptUrl: '',
  34. });
  35. export type TableListItem = {
  36. key: number;
  37. name: string;
  38. };
  39. const CheckUnitDepMap: React.FC = () => {
  40. const [treeData, set_treeData] = useState<getTreeDataRespType[]>([]);
  41. const [tableColumn, set_tableColumn] = useState<ProColumns[]>([]);
  42. const [currentSelectedTreeNode, set_currentSelectedTreeNode] = useState<any | undefined>();
  43. const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>(undefined);
  44. const [tableDataSearchKeywords, set_tableDataSearchKeywords] = useState('');
  45. const [tableData, set_tableData] = useState<CheckUnitDepMapTableDataType[]>([]);
  46. const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
  47. const [searchValue, setSearchValue] = useState('');
  48. const [autoExpandParent, setAutoExpandParent] = useState(true);
  49. const tableRef = useRef<ActionType>();
  50. const column = [
  51. {
  52. title: '科室代码',
  53. dataIndex: 'deptCode'
  54. },
  55. {
  56. title: '科室名称',
  57. dataIndex: 'deptName',
  58. },
  59. {
  60. title: '类型',
  61. dataIndex: 'deptTypeName',
  62. },
  63. {
  64. title: '操作',
  65. key: 'option',
  66. valueType: 'option',
  67. render: (_: any, record: any) => {
  68. return [
  69. <Popconfirm
  70. title="是否确认删除?"
  71. key="del"
  72. onConfirm={() => delTableDataHandle(record)}
  73. >
  74. <a>删除</a>
  75. </Popconfirm>
  76. ]
  77. },
  78. },
  79. ];
  80. const delTableDataHandle = async (record: any) => {
  81. const resp: any = await delTableData(record.id);
  82. if (resp) {
  83. getTreeReqFunc();
  84. tableRef.current?.reload();
  85. }
  86. }
  87. const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
  88. // console.log('selected', selectedKeys, info);
  89. const { node } = info;
  90. set_currentSelectedTreeNode(node);
  91. };
  92. const getTableData = async (type: 'PERSON' | 'DEP' | 'CHARGE', params: any, sort: any, filter: any) => {
  93. // console.log({ currentSelectedTreeNode });
  94. // console.log({ params, sort, filter });
  95. if (currentSelectedTreeNode) {
  96. const resp = await getCheckUnitDepMapTableDataByUnitClass({
  97. ...params,
  98. unitCode: currentSelectedTreeNode.code,
  99. });
  100. if (resp) {
  101. set_tableData(resp.list);
  102. return {
  103. data: resp.list,
  104. success: true,
  105. total: resp.totalCount,
  106. pageSize: resp.pageSize,
  107. totalPage: resp.totalPage,
  108. }
  109. }
  110. return {
  111. data: [],
  112. success: true
  113. }
  114. }
  115. return []
  116. }
  117. interface DataType {
  118. key: string;
  119. title: string;
  120. description: string;
  121. disabled: boolean;
  122. tag: string;
  123. }
  124. const transferTableColumn: any[] = [
  125. {
  126. title: '科室名称',
  127. dataIndex: 'deptName',
  128. with: 100,
  129. ellipsis: true,
  130. },
  131. {
  132. title: '科室类型',
  133. width: 120,
  134. dataIndex: 'deptTypeName',
  135. ellipsis: true
  136. },
  137. ];
  138. const addHandle = () => {
  139. const ref = React.createRef<{ save: any; }>();
  140. Modal.confirm({
  141. title: `添加单元对照科室(${currentSelectedTreeNode.name})`,
  142. icon: <></>,
  143. width: 750,
  144. centered: true,
  145. okText:'确定',
  146. cancelText:'取消',
  147. content: <TableTransfer
  148. ref={ref}
  149. record={currentSelectedTreeNode}
  150. leftColumns={transferTableColumn}
  151. rightColumns={transferTableColumn} dataSource={[]}
  152. ></TableTransfer>,
  153. onOk: () => {
  154. return ref.current && ref.current.save();
  155. }
  156. })
  157. }
  158. const TableTransfer = React.forwardRef(({ leftColumns, rightColumns, record }: any, ref) => {
  159. const [targetKeys, setTargetKeys] = useState<string[]>([]);
  160. const [datasource, set_datasource] = useState<any[]>([]);
  161. const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
  162. //获取对照科室
  163. const getFuncList = async () => {
  164. const resp = await getMapDepList();
  165. if (resp) {
  166. const defaultSelctedkeys = tableData.map((item: any) => item.deptCode);
  167. const hasSelectedData = tableData.map((a: any) => ({ deptCode: a.deptCode, deptName: a.deptName, deptType: a.deptType, deptTypeName: a.deptTypeName }))
  168. set_datasource([...resp, ...hasSelectedData]);
  169. setTargetKeys(defaultSelctedkeys);
  170. }
  171. }
  172. const onChange = (nextTargetKeys: string[]) => {
  173. setTargetKeys(nextTargetKeys);
  174. };
  175. const onSelectChange = (sourceSelectedKeys: string[], targetSelectedKeys: string[]) => {
  176. //console.log('sourceSelectedKeys:', sourceSelectedKeys,'targetSelectedKeys:',targetSelectedKeys);
  177. setSelectedKeys([...sourceSelectedKeys, ...targetSelectedKeys]);
  178. };
  179. useImperativeHandle(ref, () => ({
  180. save: async () => {
  181. const needData = datasource.filter(item => targetKeys.includes(item.deptCode));
  182. const result = {
  183. unitCode: record.code,
  184. unitType: record.unitType,
  185. department: needData
  186. };
  187. const resp = await addMapDep(result);
  188. if (resp) {
  189. message.success('添加成功!');
  190. getTreeReqFunc();
  191. tableRef.current?.reload();
  192. }
  193. }
  194. }));
  195. useEffect(() => {
  196. getFuncList();
  197. }, [])
  198. return (
  199. <Transfer className='TableTransfer' showSearch
  200. titles={['待选项', '已选项']}
  201. locale={{
  202. itemUnit: '项',
  203. itemsUnit: '项',
  204. searchPlaceholder: '请输入'
  205. }}
  206. onChange={onChange}
  207. onSelectChange={onSelectChange}
  208. dataSource={datasource}
  209. rowKey={record => record.deptCode}
  210. targetKeys={targetKeys}
  211. selectedKeys={selectedKeys}
  212. filterOption={(inputValue, item) => {
  213. return item.deptName!.indexOf(inputValue) !== -1
  214. }}
  215. >
  216. {({
  217. direction,
  218. filteredItems,
  219. onItemSelectAll,
  220. onItemSelect,
  221. selectedKeys: listSelectedKeys,
  222. disabled: listDisabled,
  223. }) => {
  224. // console.log({ filteredItems, listSelectedKeys,direction });
  225. const columns = direction === 'left' ? leftColumns : rightColumns;
  226. const rowSelection: TableRowSelection<TransferItem> = {
  227. getCheckboxProps: (item) => ({ disabled: listDisabled || item.disabled }),
  228. onSelectAll(selected, selectedRows) {
  229. const treeSelectedKeys = selectedRows.map(({ deptCode }) => deptCode);
  230. const diffKeys = selected
  231. ? difference(treeSelectedKeys, listSelectedKeys)
  232. : difference(listSelectedKeys, treeSelectedKeys);
  233. onItemSelectAll(diffKeys as string[], selected);
  234. },
  235. onSelect({ deptCode }, selected) {
  236. onItemSelect(deptCode as string, selected);
  237. },
  238. selectedRowKeys: listSelectedKeys,
  239. };
  240. return (
  241. <Table
  242. rowSelection={rowSelection}
  243. columns={columns as TransferItem[]}
  244. dataSource={filteredItems}
  245. size="small"
  246. rowKey={'deptCode'}
  247. style={{ pointerEvents: listDisabled ? 'none' : undefined }}
  248. onRow={({ deptCode, disabled: itemDisabled }) => ({
  249. onClick: () => {
  250. if (itemDisabled || listDisabled) return;
  251. onItemSelect(deptCode as string, !listSelectedKeys.includes(deptCode as string));
  252. },
  253. })}
  254. />
  255. );
  256. }}
  257. </Transfer>
  258. )
  259. })
  260. const dataList: any[] = [];
  261. const getParentKey = (key: React.Key, tree: any[]): React.Key => {
  262. let parentKey: React.Key;
  263. for (let i = 0; i < tree.length; i++) {
  264. const node = tree[i];
  265. if (node.child) {
  266. if (node.child.some((item: { code: React.Key; }) => item.code === key)) {
  267. parentKey = node.code;
  268. } else if (getParentKey(key, node.child)) {
  269. parentKey = getParentKey(key, node.child);
  270. }
  271. }
  272. }
  273. return parentKey!;
  274. };
  275. const onTreeSearchKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  276. const { value } = e.target;
  277. const newExpandedKeys = dataList
  278. .map((item) => {
  279. if (item.name.indexOf(value) > -1) {
  280. return getParentKey(item.code, treeData);
  281. }
  282. return null;
  283. });
  284. const b = newExpandedKeys.filter((item, i, self) => item && self.indexOf(item) === i);
  285. setExpandedKeys(newExpandedKeys as React.Key[]);
  286. setSearchValue(value);
  287. setAutoExpandParent(true);
  288. }
  289. const onExpand = (newExpandedKeys: React.Key[]) => {
  290. setExpandedKeys(newExpandedKeys);
  291. setAutoExpandParent(false);
  292. };
  293. const generateList = (data: any[]) => {
  294. for (let i = 0; i < data.length; i++) {
  295. const node = data[i];
  296. const { code, name } = node;
  297. dataList.push({ code, name });
  298. if (node.child) {
  299. generateList(node.child);
  300. }
  301. }
  302. };
  303. generateList(treeData as any);
  304. const getTreeReqFunc = async () => {
  305. const resp = await getTreeData();
  306. // function checkMap(node:any) {
  307. // if (!node.child || node.child.length === 0) {
  308. // // 如果当前节点没有子节点,说明是最里层的节点
  309. // return node.map === false;
  310. // } else {
  311. // // 如果当前节点有子节点,递归遍历所有子节点
  312. // let flag = false; //子节点是否存在false
  313. // for (let i = 0; i < node.child.length; i++) {
  314. // if (checkMap(node.child[i])) {
  315. // // 如果子节点存在 map 为 false,设置当前节点的 map 为 false
  316. // flag = true;
  317. // break;
  318. // }
  319. // }
  320. // return flag;
  321. // }
  322. // }
  323. // function setParentMapVal(node:any,val:boolean) {
  324. // if (!node.child) {
  325. // // 如果当前节点没有子节点,则直接返回
  326. // return;
  327. // }
  328. // for (let item of node.child) {
  329. // // 递归处理子节点,将子节点的 map 属性改为 false
  330. // node.map = val,
  331. // setParentMapVal(item,val);
  332. // }
  333. // }
  334. // const newData = resp.map((item: any) => {
  335. // const flag = checkMap(item);
  336. // console.log({flag});
  337. // if(flag){
  338. // //将所有父级map改为false
  339. // setParentMapVal(item,false);
  340. // }else{
  341. // setParentMapVal(item,true);
  342. // }
  343. // return item;
  344. // });
  345. set_treeData(resp);
  346. }
  347. const tableDataSearchHandle = (paramName: string) => {
  348. set_tableDataFilterParams({
  349. ...tableDataFilterParams,
  350. [`${paramName}`]: tableDataSearchKeywords
  351. })
  352. }
  353. useEffect(() => {
  354. if (currentSelectedTreeNode && currentSelectedTreeNode.unitType) {
  355. tableRef.current?.reload();
  356. }
  357. }, [currentSelectedTreeNode]);
  358. useEffect(() => {
  359. //初始化左侧树结构数据后
  360. if (currentSelectedTreeNode) return;
  361. if (treeData?.length > 0) {
  362. if (treeData[0].child && treeData[0].child.length > 0) {
  363. const [node, nodeParent] = getDeepestTreeData(treeData[0], 'child');
  364. console.log({currentSelectedTreeNode});
  365. set_currentSelectedTreeNode(node);
  366. setExpandedKeys([nodeParent.code]);
  367. }
  368. }
  369. }, [treeData]);
  370. useEffect(() => {
  371. set_tableColumn(column as ProColumns[]);
  372. getTreeReqFunc();
  373. }, []);
  374. return (
  375. <div className='CheckUnitDepMap'>
  376. <div className='leftTree'>
  377. <div className='search'>
  378. <Input
  379. className='searchInput'
  380. placeholder="请输入类目名称"
  381. size='small'
  382. allowClear
  383. style={{ marginBottom: 16 }}
  384. onChange={onTreeSearchKeyChange}
  385. suffix={
  386. <SearchIcon type='iconsousuo' />
  387. }
  388. />
  389. </div>
  390. {
  391. treeData && treeData.length > 0 && currentSelectedTreeNode && (
  392. <DirectoryTree
  393. fieldNames={{ title: 'name', key: 'code', children: 'child' }}
  394. rootStyle={{ height: '100%', paddingBottom: 50, overflowY: 'scroll', overflowX: 'hidden' }}
  395. onSelect={onSelect}
  396. onExpand={onExpand}
  397. expandedKeys={expandedKeys}
  398. autoExpandParent={autoExpandParent}
  399. selectedKeys={[currentSelectedTreeNode.code]}
  400. blockNode={true}
  401. icon={() => null}
  402. titleRender={
  403. (nodeData: any) => {
  404. const strTitle = nodeData.name as string;
  405. const index = strTitle.indexOf(searchValue);
  406. const beforeStr = strTitle.substring(0, index);
  407. const afterStr = strTitle.slice(index + searchValue.length);
  408. const title =
  409. index > -1 ? (
  410. <span>
  411. {beforeStr}
  412. <span className={'site-tree-search-value'} style={{ color: 'red', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{searchValue}</span>
  413. {afterStr}
  414. </span>
  415. ) : (
  416. <span className={'strTitle'}>{strTitle}</span>
  417. );
  418. return <div style={{
  419. display: 'flex', flexDirection: 'row',
  420. width: '100%',
  421. justifyContent: 'space-between', alignItems: 'center', height: 32,
  422. borderRadius: '4px',
  423. overflow: 'hidden',
  424. color: '#17181A',
  425. textOverflow: 'ellipsis',
  426. whiteSpace: 'nowrap'
  427. }}>
  428. {title}
  429. {!nodeData.map&&<span className={nodeData.unitType?'point lastChild':'point'}></span>}
  430. </div>
  431. }
  432. }
  433. defaultSelectedKeys={[treeData[0].child[0].code]}
  434. treeData={treeData as unknown as DataNode[]}
  435. // treeData={treeDataNew}
  436. switcherIcon={(props: any) => {
  437. const { expanded } = props;
  438. return !expanded ? <img style={{ width: 20, height: 20, position: 'relative', top: 5 }} src={expandedIcon} /> : <img style={{ width: 20, height: 20, position: 'relative', top: 5 }} src={closeIcon} />
  439. }}
  440. />
  441. )
  442. }
  443. </div>
  444. {/* <div style={{width:16,height:'92vh',background:'#F5F7FA'}}></div> */}
  445. <div className='rightContent'>
  446. <BMSPagecontainer title={false} ghost>
  447. <div className='content'>
  448. <div className='tableToolbar'>
  449. <div className='search'>
  450. <span>检索:</span><Input className='searchInput' allowClear placeholder="请输入科室名称" onChange={(e) => {
  451. set_tableDataSearchKeywords(e.target.value);
  452. if (e.target.value.length == 0) {
  453. set_tableDataFilterParams({ ...tableDataFilterParams, deptName: '' });
  454. }
  455. }} suffix={
  456. <IconFont type="iconsousuo" onClick={() => tableDataSearchHandle('deptName')} />
  457. }
  458. onPressEnter={(e) => {
  459. set_tableDataFilterParams({
  460. ...tableDataFilterParams,
  461. deptName: (e.target as HTMLInputElement).value
  462. });
  463. }}
  464. />
  465. </div>
  466. <div className='btnGroup'>
  467. <span className='add' onClick={() => addHandle()}>添加</span>
  468. </div>
  469. </div>
  470. {currentSelectedTreeNode && <BMSTable actionRef={tableRef} params={tableDataFilterParams} rowKey='id' columns={tableColumn} request={(params, sort, filter) => getTableData('CHARGE', params, sort, filter)} />}
  471. </div>
  472. </BMSPagecontainer>
  473. </div>
  474. </div>
  475. );
  476. };
  477. export default CheckUnitDepMap;