index.tsx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. /*
  2. * @Author: code4eat awesomedema@gmail.com
  3. * @Date: 2022-12-16 09:42:52
  4. * @LastEditors: code4eat awesomedema@gmail.com
  5. * @LastEditTime: 2023-08-10 15:47:05
  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, message, Popover } 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 { commitRequest, delPersonRequest, generateDataRequest, getCurrentCommitStatusReq, getPersonInfoTableData, getTotalEmps, getTreeData, getTreeDataRespType, saveEmpsRequest } from './service';
  20. import { TransferItem, TransferProps } from 'antd/es/transfer';
  21. import { getComputeDate } from '@/pages/Home/service';
  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 } from 'antd/es/table';
  27. import { TableRowSelection } from 'antd/es/table/interface';
  28. import { difference } from 'lodash';
  29. import { getCurrentCheckStatus } from '@/services/auth';
  30. import { getJiezhuanStatus } from '@/pages/budgetMana/monthlySet/service';
  31. const IconFont = createFromIconfontCN({
  32. scriptUrl: '',
  33. });
  34. const SearchIcon = createFromIconfontCN({
  35. scriptUrl: '',
  36. });
  37. export type TableListItem = {
  38. key: number;
  39. name: string;
  40. };
  41. const EmployeeInfoCheck: React.FC = () => {
  42. const [treeData, set_treeData] = useState<getTreeDataRespType[]>([]);
  43. const [tableColumn, set_tableColumn] = useState<ProColumns[]>([]);
  44. const [currentSelectedTreeNode, set_currentSelectedTreeNode] = useState<any | undefined>();
  45. const [currentComputeDate, set_currentComputeDate] = useState<string | undefined>();
  46. const [ifShowTip, set_ifShowTip] = useState(false);
  47. const [commitStatus, set_commitStatus] = useState('0'); //提交状态
  48. const [empInfoSearchKeywords, set_empInfoSearchKeywords] = useState('');
  49. const [empFilterParams, set_empFilterParams] = useState<any | undefined>(undefined);
  50. const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
  51. const [searchValue, setSearchValue] = useState('');
  52. const [autoExpandParent, setAutoExpandParent] = useState(true);
  53. const [auditType, set_auditType] = useState('0'); //审核状态
  54. const [ifBanAllAction, set_ifBanAllAction] = useState(true); //是否掩藏所有操作
  55. const tableRef = useRef<ActionType>();
  56. const column = [
  57. {
  58. title: '工号',
  59. dataIndex: 'empNo',
  60. key: 'empNo',
  61. },
  62. {
  63. title: '姓名',
  64. dataIndex: 'empName',
  65. key: 'empName',
  66. },
  67. {
  68. title: '职务',
  69. dataIndex: 'jobTitleName',
  70. key: 'jobTitleName',
  71. },
  72. {
  73. title: '职称',
  74. dataIndex: 'titleName',
  75. },
  76. {
  77. title: '职称系数',
  78. dataIndex: 'titleRate',
  79. },
  80. {
  81. title: '岗位',
  82. dataIndex: 'jobTitleName',
  83. key: 'jobTitleName',
  84. },
  85. {
  86. title: '岗位系数',
  87. dataIndex: 'positionRate',
  88. key: 'positionRate',
  89. },
  90. {
  91. title: '年资',
  92. dataIndex: 'seniority',
  93. key: 'seniority',
  94. },
  95. ];
  96. const delPersonHandle = async (record: any) => {
  97. const { props: { record: { id } } } = record;
  98. const currentComputeDateResp = await getComputeDate();
  99. if (currentComputeDateResp) {
  100. const resp = await delPersonRequest({
  101. computeDate: currentComputeDateResp,
  102. id: id,
  103. unitCode: currentSelectedTreeNode.code
  104. });
  105. if (resp) {
  106. tableRef.current?.reload();
  107. }
  108. }
  109. }
  110. const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
  111. // console.log('selected', selectedKeys, info);
  112. const { node } = info;
  113. set_currentSelectedTreeNode(node);
  114. };
  115. const getCurrentComputeDate = async () => {
  116. const resp = await getComputeDate();
  117. set_currentComputeDate(resp);
  118. }
  119. const getCheckStatus = async (computeDate: string) => {
  120. const resp = await getCurrentCheckStatus(computeDate);
  121. if (resp) {
  122. set_auditType(`${resp}`); //0 未审核 1 已审核
  123. }
  124. }
  125. const getCurrentCommitStatus = async () => {
  126. if (currentSelectedTreeNode) {
  127. const resp = await getCurrentCommitStatusReq({
  128. computeDate: currentComputeDate as string,
  129. unitCode: currentSelectedTreeNode.code
  130. });
  131. set_commitStatus(`${resp}`);
  132. }
  133. }
  134. const getTableData = async (type: 'PERSON' | 'DEP' | 'CHARGE', params: any, sort: any, filter: any) => {
  135. // console.log({ currentSelectedTreeNode });
  136. // console.log({ params, sort, filter });
  137. if (currentSelectedTreeNode && currentComputeDate) {
  138. if (type == 'PERSON') {
  139. const resp = await getPersonInfoTableData({
  140. computeDate: currentComputeDate,
  141. unitCode: currentSelectedTreeNode.code,
  142. ...params,
  143. });
  144. if (resp) {
  145. return {
  146. data: resp.list,
  147. success: true,
  148. total: resp.totalCount,
  149. pageSize: resp.pageSize,
  150. totalPage: resp.totalPage,
  151. }
  152. }
  153. return {
  154. data: [],
  155. success: true
  156. }
  157. }
  158. }
  159. return []
  160. }
  161. interface TableTransferProps extends TransferProps<TransferItem> {
  162. leftColumns: ColumnsType<any>;
  163. rightColumns: ColumnsType<any>;
  164. }
  165. const transferTableColumn = [
  166. {
  167. title: '工号',
  168. dataIndex: 'empNo',
  169. },
  170. {
  171. title: '姓名',
  172. dataIndex: 'name',
  173. },
  174. ]
  175. const addPersonFunc = () => {
  176. const ref = React.createRef<{ save: any }>();
  177. Modal.confirm({
  178. title: '添加人员',
  179. icon: <></>,
  180. width: 640,
  181. okText: '确定',
  182. cancelText: '取消',
  183. content: <TableTransfer
  184. ref={ref}
  185. leftColumns={transferTableColumn}
  186. rightColumns={transferTableColumn}
  187. ></TableTransfer>,
  188. onOk: () => {
  189. return ref.current && ref.current.save();
  190. }
  191. })
  192. }
  193. const TableTransfer = React.forwardRef(({ leftColumns, rightColumns, ...restProps }: TableTransferProps, ref) => {
  194. const [_data, _set_data] = useState<any>();
  195. const [targetKeys, setTargetKeys] = useState<string[]>([]);
  196. const [datasource, set_datasource] = useState<any[]>([]);
  197. const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
  198. //获取单元
  199. const getFuncList = async () => {
  200. const resp = await getTotalEmps({
  201. computeDate: currentComputeDate as string,
  202. unitCode: currentSelectedTreeNode.code
  203. });
  204. if (resp) {
  205. //_set_data(resp);
  206. const allData = resp.allEmployees.concat(resp.checkEmployees);
  207. set_datasource(allData);
  208. const defaultSelctedkeys = resp.checkEmployees.map((item: any) => item.empNo);
  209. setTargetKeys(defaultSelctedkeys);
  210. }
  211. }
  212. const onChange = (nextTargetKeys: string[]) => {
  213. setTargetKeys(nextTargetKeys);
  214. };
  215. const onSelectChange = (sourceSelectedKeys: string[], targetSelectedKeys: string[]) => {
  216. //console.log('sourceSelectedKeys:', sourceSelectedKeys,'targetSelectedKeys:',targetSelectedKeys);
  217. setSelectedKeys([...sourceSelectedKeys, ...targetSelectedKeys]);
  218. };
  219. useImperativeHandle(ref, () => ({
  220. save: async () => {
  221. const items = datasource.filter(a => targetKeys.includes(a.empNo));
  222. const resp = await saveEmpsRequest(items);
  223. if (resp) {
  224. tableRef.current?.reload();
  225. }
  226. }
  227. }));
  228. useEffect(() => {
  229. getFuncList();
  230. }, [])
  231. return (
  232. <Transfer className='TableTransfer' showSearch
  233. titles={['待选项', '已选项']}
  234. locale={{
  235. itemUnit: '项',
  236. itemsUnit: '项',
  237. searchPlaceholder: '请输入',
  238. }}
  239. oneWay={true}
  240. onChange={onChange}
  241. onSelectChange={onSelectChange}
  242. dataSource={datasource}
  243. rowKey={record => record.empNo}
  244. targetKeys={targetKeys}
  245. selectedKeys={selectedKeys}
  246. filterOption={(inputValue, item) => {
  247. return item.name!.indexOf(inputValue) !== -1
  248. }}
  249. >
  250. {({
  251. direction,
  252. filteredItems,
  253. onItemSelectAll,
  254. onItemSelect,
  255. selectedKeys: listSelectedKeys,
  256. disabled: listDisabled,
  257. }) => {
  258. // console.log({ filteredItems, listSelectedKeys,direction });
  259. const columns = direction === 'left' ? leftColumns : rightColumns;
  260. const rowSelection: TableRowSelection<TransferItem> = {
  261. getCheckboxProps: (item) => ({ disabled: listDisabled || item.disabled }),
  262. onSelectAll(selected, selectedRows) {
  263. const treeSelectedKeys = selectedRows.map(({ empNo }) => empNo);
  264. const diffKeys = selected
  265. ? difference(treeSelectedKeys, listSelectedKeys)
  266. : difference(listSelectedKeys, treeSelectedKeys);
  267. onItemSelectAll(diffKeys as string[], selected);
  268. },
  269. onSelect({ empNo }, selected) {
  270. onItemSelect(empNo as string, selected);
  271. },
  272. selectedRowKeys: listSelectedKeys,
  273. };
  274. return (
  275. <BMSTable
  276. rowSelection={rowSelection}
  277. columns={columns as TransferItem[]}
  278. dataSource={filteredItems}
  279. size="small"
  280. bordered={false}
  281. rowKey={'empNo'}
  282. pagination={{ showTitle: false, pageSize: 9, showLessItems: false, simple: true, showTotal: () => false }}
  283. tableAlertRender={false}
  284. style={{ pointerEvents: listDisabled ? 'none' : undefined }}
  285. onRow={({ empNo, disabled: itemDisabled }) => ({
  286. onClick: () => {
  287. if (itemDisabled || listDisabled) return;
  288. onItemSelect(empNo as string, !listSelectedKeys.includes(empNo as string));
  289. },
  290. })}
  291. />
  292. );
  293. }}
  294. </Transfer>
  295. )
  296. })
  297. const confirmGenerateHandle = async () => {
  298. const resp = await generateDataRequest({
  299. computeDate: currentComputeDate as string,
  300. unitCode: currentSelectedTreeNode.code
  301. });
  302. if (resp) {
  303. message.success('生成数据成功');
  304. tableRef.current?.reload();
  305. } else {
  306. message.success('生成数据错误!');
  307. }
  308. }
  309. const generateFunc = () => {
  310. let msg = '生成操作会覆盖已有的数据,是否继续?';
  311. Modal.confirm({
  312. title: '注意',
  313. okText: '确定',
  314. cancelText: '取消',
  315. content: msg,
  316. onOk: () => confirmGenerateHandle()
  317. });
  318. }
  319. const commitBtnhandle = () => {
  320. Modal.confirm({
  321. title: '注意',
  322. okText: '确定',
  323. cancelText: '取消',
  324. content: `${commitStatus == '1' ? '取消提交' : '提交'}当前选择的核算单元的数据?`,
  325. onOk: async () => {
  326. const resp = await commitRequest({
  327. computeDate: currentComputeDate as string,
  328. unitCode: currentSelectedTreeNode.code,
  329. type: commitStatus == '1' ? '0' : '1', //1 提交 0 取消
  330. });
  331. if (resp) {
  332. message.success('提交成功!');
  333. getTreeReqFunc(currentComputeDate as string);
  334. getCurrentCommitStatus();
  335. }
  336. }
  337. })
  338. }
  339. const searchEmpHandle = () => {
  340. set_empFilterParams({
  341. empInfo: empInfoSearchKeywords
  342. })
  343. // tableRef.current?.reload();
  344. }
  345. const dataList: any[] = [];
  346. const getParentKey = (key: React.Key, tree: any[]): React.Key => {
  347. let parentKey: React.Key;
  348. for (let i = 0; i < tree.length; i++) {
  349. const node = tree[i];
  350. if (node.child) {
  351. if (node.child.some((item: { code: React.Key; }) => item.code === key)) {
  352. parentKey = node.code;
  353. } else if (getParentKey(key, node.child)) {
  354. parentKey = getParentKey(key, node.child);
  355. }
  356. }
  357. }
  358. return parentKey!;
  359. };
  360. const onTreeSearchKeyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  361. const { value } = e.target;
  362. const newExpandedKeys = dataList
  363. .map((item) => {
  364. if (item.name.indexOf(value) > -1) {
  365. return getParentKey(item.code, treeData);
  366. }
  367. return null;
  368. });
  369. const b = newExpandedKeys.filter((item, i, self) => item && self.indexOf(item) === i);
  370. setExpandedKeys(newExpandedKeys as React.Key[]);
  371. setSearchValue(value);
  372. setAutoExpandParent(true);
  373. }
  374. const onExpand = (newExpandedKeys: React.Key[]) => {
  375. setExpandedKeys(newExpandedKeys);
  376. setAutoExpandParent(false);
  377. };
  378. const generateList = (data: getTreeDataRespType[]) => {
  379. for (let i = 0; i < data.length; i++) {
  380. const node = data[i];
  381. const { code, name } = node;
  382. dataList.push({ code: code, name: name });
  383. if (node.child) {
  384. generateList(node.child);
  385. }
  386. }
  387. };
  388. generateList(treeData as any);
  389. const getTreeReqFunc = async (computeDate: string) => {
  390. const resp = await getTreeData(computeDate);
  391. set_treeData(resp);
  392. }
  393. const getJiezhuanStatusHandle = async () => {
  394. const resp = await getJiezhuanStatus(currentComputeDate as string);
  395. if (resp == 2) {
  396. set_ifBanAllAction(true);
  397. } else {
  398. set_ifBanAllAction(false);
  399. }
  400. }
  401. useEffect(() => {
  402. if (currentComputeDate) {
  403. getTreeReqFunc(currentComputeDate);
  404. getCheckStatus(currentComputeDate);
  405. getJiezhuanStatusHandle();
  406. }
  407. }, [currentComputeDate]);
  408. useEffect(() => {
  409. tableRef.current?.reload();
  410. if (currentComputeDate && currentSelectedTreeNode) {
  411. getCurrentCommitStatus();
  412. }
  413. }, [currentSelectedTreeNode, currentComputeDate]);
  414. useEffect(() => {
  415. //初始化左侧树结构数据后
  416. if (treeData?.length > 0) {
  417. if (!currentSelectedTreeNode) {
  418. if (treeData[0].child && treeData[0].child.length > 0) {
  419. const [node, nodeParent] = getDeepestTreeData(treeData[0], 'child');
  420. set_currentSelectedTreeNode(node);
  421. setExpandedKeys([nodeParent.code]);
  422. getCurrentCommitStatus();
  423. }
  424. }
  425. }
  426. }, [treeData]);
  427. useEffect(() => {
  428. console.log({ auditType });
  429. if (auditType == '1') {
  430. //当审核中时,禁掉所有操作
  431. set_commitStatus('1');
  432. }
  433. }, [auditType])
  434. useEffect(() => {
  435. set_tableColumn(column as ProColumns[]);
  436. getCurrentComputeDate();
  437. }, []);
  438. return (
  439. <div className='EmployeeInfoCheck'>
  440. <div className='leftTree'>
  441. <div className='search'>
  442. <Input
  443. className='searchInput'
  444. placeholder="请输入类目名称"
  445. size='small'
  446. allowClear
  447. style={{ marginBottom: 16 }}
  448. onChange={onTreeSearchKeyChange}
  449. suffix={
  450. <SearchIcon type='iconsousuo' />
  451. }
  452. />
  453. </div>
  454. {
  455. treeData && treeData.length > 0 && currentSelectedTreeNode && (
  456. <DirectoryTree
  457. fieldNames={{ title: 'name', key: 'code', children: 'child' }}
  458. rootStyle={{ height: '100%', paddingBottom: 50, overflowY: 'scroll', overflowX: 'hidden' }}
  459. onSelect={onSelect}
  460. onExpand={onExpand}
  461. expandedKeys={expandedKeys}
  462. autoExpandParent={autoExpandParent}
  463. selectedKeys={[currentSelectedTreeNode.code]}
  464. blockNode={true}
  465. icon={() => null}
  466. titleRender={
  467. (nodeData: any) => {
  468. const strTitle = nodeData.name as string;
  469. const index = strTitle.indexOf(searchValue);
  470. const beforeStr = strTitle.substring(0, index);
  471. const afterStr = strTitle.slice(index + searchValue.length);
  472. const title =
  473. index > -1 ? (
  474. <span>
  475. {beforeStr}
  476. <span className="site-tree-search-value" style={{ color: '#3377ff', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{searchValue}</span>
  477. {afterStr}
  478. </span>
  479. ) : (
  480. <span className='strTitle'>{strTitle}</span>
  481. );
  482. return <div style={{
  483. display: 'flex', flexDirection: 'row',
  484. width: '100%',
  485. justifyContent: 'space-between', alignItems: 'center', height: 32,
  486. borderRadius: '4px',
  487. overflow: 'hidden',
  488. color: '#17181A',
  489. textOverflow: 'ellipsis',
  490. whiteSpace: 'nowrap'
  491. }}>
  492. {title}
  493. {!nodeData.map && <span className={nodeData.unitType ? 'point lastChild' : 'point'}></span>}
  494. </div>
  495. }
  496. }
  497. defaultSelectedKeys={[treeData[0].child[0].code]}
  498. treeData={treeData as unknown as DataNode[]}
  499. // treeData={treeDataNew}
  500. switcherIcon={(props: any) => {
  501. const { expanded } = props;
  502. 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} />
  503. }}
  504. />
  505. )
  506. }
  507. </div>
  508. {/* <div style={{width:16,height:'92vh',background:'#F5F7FA'}}></div> */}
  509. <div className='rightContent'>
  510. <BMSPagecontainer title={`核算年月:${currentComputeDate}`} ghost>
  511. <div className='tabContent'>
  512. <div className='tableToolbar'>
  513. <div className='search'>
  514. <span>人员:</span><Input className='searchInput' allowClear onChange={(e) => {
  515. set_empInfoSearchKeywords(e.target.value)
  516. if (e.target.value.length == 0) {
  517. set_empFilterParams({
  518. ...empFilterParams,
  519. empInfo: ''
  520. });
  521. }
  522. }} placeholder="输入工号/姓名" suffix={
  523. <IconFont type="iconsousuo" onClick={() => searchEmpHandle()} />
  524. } />
  525. </div>
  526. <div className='btnGroupWrap'>
  527. {
  528. !ifBanAllAction && (
  529. <Popover open={ifShowTip} content={'当前处于提交中,无法操作!'} >
  530. <div className={commitStatus != '0'? 'btnGroup disabled' : 'btnGroup'}
  531. /**
  532. * 当审核中,三个操作按钮都不可点击
  533. * 当非审核中,生成和添加不可操作
  534. */
  535. onMouseEnter={() => commitStatus == '1' ? set_ifShowTip(true) : set_ifShowTip(false)}
  536. onMouseLeave={() => set_ifShowTip(false)}
  537. >
  538. <span key="2" onClick={commitStatus == '0'? () => generateFunc() : () => { }}>生成</span>
  539. <span key="3" onClick={commitStatus == '0'? () => addPersonFunc() : () => { }}>添加</span>
  540. </div>
  541. </Popover>
  542. )
  543. }
  544. {!ifBanAllAction&&<div key="4" className={'commit'} onClick={() => commitBtnhandle() }>{commitStatus == '1' ? '取消提交' : '提交'}</div>}
  545. </div>
  546. </div>
  547. {currentSelectedTreeNode && <BMSTable params={empFilterParams} actionRef={tableRef} rowKey='empNo' columns={commitStatus == '0' ? [...tableColumn, {
  548. title: '操作',
  549. key: 'option',
  550. valueType: 'option',
  551. render: (record: any) => [
  552. <Popconfirm key="popconfirm" title={`确认删除吗?`} okText="是" cancelText="否" onConfirm={() => delPersonHandle(record)}>
  553. <a key={'del'}>删除</a>
  554. </Popconfirm>
  555. ],
  556. },] : [...tableColumn]} request={(params, sort, filter) => getTableData('PERSON', params, sort, filter)} />}
  557. </div>
  558. </BMSPagecontainer>
  559. </div >
  560. </div >
  561. );
  562. };
  563. export default EmployeeInfoCheck;