index.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. /*
  2. * @Author: code4eat awesomedema@gmail.com
  3. * @Date: 2023-01-04 14:12:31
  4. * @LastEditors: code4eat awesomedema@gmail.com
  5. * @LastEditTime: 2023-07-24 13:30:07
  6. * @FilePath: /BudgetManaSystem/src/pages/budgetMana/oneBatch/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 { BMSTable } from '@/components/BMSTable';
  11. import { getComputeDate } from '@/pages/Home/service';
  12. import { ActionType, ProColumns } from '@ant-design/pro-components';
  13. import { message, Modal, Popover, Table, Tabs, Button } from 'antd';
  14. import { useEffect, useRef, useState } from 'react';
  15. import { caculate, checkRequest, getCheckType, getCurrentCheckStatus, getCurrentZhileiCheckStatus, getData, getZhileiList } from './service';
  16. import './style.less';
  17. import { create, all, number } from 'mathjs'
  18. import exportTableToExcel from '@/utils/tableToExcel';
  19. import { convertToColumns } from '@/utils/tooljs';
  20. import * as XLSX from 'xlsx';
  21. import exportTableToMultiExcel from '@/utils/tableToMultiHeaderExcel';
  22. import { getJiezhuanStatus } from '../monthlySet/service';
  23. const config = {
  24. number: 'number',
  25. precision: 14
  26. }
  27. const math = create(all, config as any);
  28. type JsonStructure = {
  29. code: string;
  30. name: string;
  31. expand?: number;
  32. childTitle?: JsonStructure[];
  33. };
  34. type Column = {
  35. title: string | JSX.Element;
  36. dataIndex: string;
  37. key: string;
  38. ellipsis: boolean;
  39. width: number;
  40. children?: Column[];
  41. };
  42. const mockData = [
  43. {
  44. "code": "1",
  45. "name": "单元绩效点值1",
  46. "sort": 1,
  47. "redirect": false,
  48. "expand": 1,
  49. "childTitle": [
  50. {
  51. "code": "2-1",
  52. "name": "单元绩效点值2",
  53. "sort": 1,
  54. "redirect": false,
  55. "expand": 0,
  56. "childTitle": [
  57. {
  58. "code": "3",
  59. "name": "单元绩效点值3",
  60. "sort": 1,
  61. "redirect": false,
  62. "expand": 1,
  63. "childTitle": []
  64. }
  65. ]
  66. },
  67. {
  68. "code": "2-2",
  69. "name": "单元绩效点值2",
  70. "sort": 1,
  71. "redirect": false,
  72. "expand": 1,
  73. "childTitle": [
  74. {
  75. "code": "3-1",
  76. "name": "单元绩效点值3-1",
  77. "sort": 1,
  78. "redirect": false,
  79. "expand": 1,
  80. "childTitle": []
  81. },
  82. {
  83. "code": "3-2",
  84. "name": "单元绩效点值3-2",
  85. "sort": 1,
  86. "redirect": false,
  87. "expand": 1,
  88. "childTitle": []
  89. }
  90. ]
  91. }
  92. ]
  93. },
  94. {
  95. "code": "4",
  96. "name": "单元管理绩效",
  97. "sort": 2,
  98. "redirect": false
  99. },
  100. ]
  101. const mockColumns = [
  102. {
  103. dataIndex: "unitName",
  104. ellipsis: true,
  105. fixed: "left",
  106. key: "unitName",
  107. title: "核算单元",
  108. width: 140
  109. },
  110. {
  111. dataIndex: "key-23",
  112. ellipsis: true,
  113. key: "key-23",
  114. title: "单元管理绩效",
  115. width: 200
  116. },
  117. {
  118. dataIndex: "key-1646350087779131392",
  119. ellipsis: true,
  120. key: "key-1646350087779131392",
  121. title: "核算年月",
  122. width: 200
  123. },
  124. {
  125. dataIndex: "key-1679019958216040448",
  126. ellipsis: true,
  127. key: "key-1679019958216040448",
  128. title: "临床诊察积分奖金",
  129. width: 200,
  130. children: [
  131. {
  132. dataIndex: "key-6",
  133. ellipsis: true,
  134. key: "key-6",
  135. title: "临床诊察积分-节假日奖金",
  136. width: 200,
  137. children: [
  138. {
  139. dataIndex: "key-4",
  140. ellipsis: true,
  141. key: "key-4",
  142. title: "临床诊察积分-节假日",
  143. width: 200
  144. },
  145. {
  146. dataIndex: "key-5",
  147. ellipsis: true,
  148. key: "key-5",
  149. title: "临床诊察积分-节假日点值",
  150. width: 200
  151. }
  152. ]
  153. },
  154. {
  155. dataIndex: "key-3",
  156. ellipsis: true,
  157. key: "key-3",
  158. title: "临床诊察积分-工作日奖金",
  159. width: 200,
  160. children: [
  161. {
  162. dataIndex: "key-1",
  163. ellipsis: true,
  164. key: "key-1",
  165. title: "临床诊察积分-工作日",
  166. width: 200
  167. },
  168. {
  169. dataIndex: "key-2",
  170. ellipsis: true,
  171. key: "key-2",
  172. title: "临床诊察积分-工作日点值",
  173. width: 200
  174. }
  175. ]
  176. }
  177. ]
  178. },
  179. {
  180. dataIndex: "totalScore",
  181. ellipsis: true,
  182. fixed: "right",
  183. key: "totalScore",
  184. title: "总奖金",
  185. width: 140
  186. }
  187. ]
  188. let checkStatusArr: number[] = []
  189. const OneBatch = () => {
  190. const [tableColumn, set_tableColumn] = useState<ProColumns[] | any[]>([]);
  191. const [columnsForExcel, set_columnsForExcel] = useState<ProColumns[] | any[]>([]);
  192. const [tableData, set_tableData] = useState<any[]>([]);
  193. const [currentComputeDate, set_currentComputeDate] = useState<string | undefined>();
  194. const [currentTabKey, set_currentTabKey] = useState('1');
  195. const [tabs, set_Tabs] = useState<{
  196. name: any; value: string
  197. }[]>([]);
  198. const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>({ reportCode: undefined });
  199. const [auditType, set_auditType] = useState('0');
  200. const [ifShowTip, set_ifShowTip] = useState(false);
  201. const tableRef = useRef<ActionType>();
  202. const [tableH, set_tableH] = useState(0);
  203. const [reportTitle, set_reportTitle] = useState('');
  204. const [checkType, set_checkType] = useState<string | undefined>(undefined); // 一次分配审核模式,1职类一起审核 2职类分开审核
  205. const [disAccount, set_disAccount] = useState(false);
  206. const [ifBanAllAction, set_ifBanAllAction] = useState(true); //是否掩藏所有操作
  207. const onTabChange = (activeKey: string) => {
  208. set_currentTabKey(activeKey);
  209. set_tableDataFilterParams({
  210. ...tableDataFilterParams,
  211. reportCode: activeKey
  212. });
  213. }
  214. const [expandedkeys, set_expandedkeys] = useState<string[]>([]);
  215. const convertToColumnsFunc = (json: JsonStructure[], ifRender?: boolean, level: number = 1): Column[] => {
  216. return json.map((item) => {
  217. let column: Column = {
  218. title: item.name,
  219. dataIndex: `key-${item.code}`,
  220. key: `key-${item.code}`,
  221. ellipsis: true,
  222. width: 200,
  223. };
  224. const isExpanded = expandedkeys.includes(item.code);
  225. if (item.expand === 1 && item.childTitle && item.childTitle.length > 0) {
  226. column.title = ifRender ? (
  227. <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'center', alignContent: 'center' }}>
  228. <span style={{ display: 'inline-block', maxWidth: '80%', minWidth: 140, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{item.name}</span>
  229. <div className='expandIcon' onClick={() => {
  230. if (isExpanded) {
  231. let _expandedkeys = [...expandedkeys];
  232. _expandedkeys.splice(expandedkeys.findIndex(a => a == item.code), 1);
  233. set_expandedkeys([..._expandedkeys]);
  234. } else {
  235. set_expandedkeys([...expandedkeys, item.code]);
  236. }
  237. }}>
  238. {isExpanded ? '-' : '+'}
  239. </div>
  240. </div>
  241. ) : item.name;
  242. if (isExpanded) {
  243. column.children = convertToColumnsFunc(item.childTitle, ifRender, level + 1);
  244. }
  245. }
  246. return column;
  247. });
  248. }
  249. useEffect(() => {
  250. tableRef.current?.reload();
  251. }, [expandedkeys])
  252. const getTableData = async (params: any, sort: any, filter: any) => {
  253. const { reportCode = 1 } = params;
  254. const resp: any = await getData(
  255. currentComputeDate as string,
  256. reportCode
  257. );
  258. if (resp) {
  259. const { title, assignmentData, reportName } = resp;
  260. const columns = convertToColumnsFunc(title, true);
  261. const columnsForExcel = convertToColumnsFunc(title, false);
  262. set_tableColumn([
  263. {
  264. title: '核算单元代码',
  265. dataIndex: 'unitCode',
  266. key: 'unitCode',
  267. width: 140,
  268. fixed: 'left',
  269. ellipsis: true,
  270. sorter: (a, b) => {
  271. return b.unitCode.localeCompare(a.unitCode)
  272. },
  273. },
  274. {
  275. title: '核算单元',
  276. dataIndex: 'unitName',
  277. key: 'unitName',
  278. width: 140,
  279. fixed: 'left',
  280. ellipsis: true,
  281. sorter: (a, b) => {
  282. return b.unitName.localeCompare(a.unitName)
  283. },
  284. },
  285. ...columns, {
  286. title: '总奖金',
  287. dataIndex: 'totalScore',
  288. key: 'totalScore',
  289. width: 140,
  290. fixed: 'right',
  291. ellipsis: true
  292. }]);
  293. set_columnsForExcel([
  294. {
  295. title: '核算单元代码',
  296. dataIndex: 'unitCode',
  297. key: 'unitCode',
  298. width: 140,
  299. fixed: 'left',
  300. ellipsis: true,
  301. sorter: (a, b) => {
  302. return b.unitCode.localeCompare(a.unitCode)
  303. },
  304. },
  305. {
  306. title: '核算单元',
  307. dataIndex: 'unitName',
  308. key: 'unitName',
  309. width: 140,
  310. fixed: 'left',
  311. ellipsis: true,
  312. sorter: (a, b) => {
  313. return b.unitName.localeCompare(a.unitName)
  314. },
  315. },
  316. ...columnsForExcel, {
  317. title: '总奖金',
  318. dataIndex: 'totalScore',
  319. key: 'totalScore',
  320. width: 140,
  321. fixed: 'right',
  322. ellipsis: true
  323. }]);
  324. // set_tableColumn([...mockColumns]);
  325. // set_columnsForExcel([...mockColumns]);
  326. const data = assignmentData.map((item: any) => {
  327. let rowData: { [key: string]: any } = {};
  328. for (let index = 0; index < item.value.length; index++) {
  329. for (const key in item.value[index]) {
  330. if (key != 'code') {
  331. rowData[`key-${item.value[index].code}`] = item.value[index].value
  332. }
  333. }
  334. }
  335. return { ...item, ...rowData, id: item.id, columns }
  336. });
  337. set_tableData(data);
  338. set_reportTitle(reportName);
  339. return {
  340. data,
  341. success: true
  342. }
  343. }
  344. return []
  345. }
  346. const checkHandle = async (type: string) => {
  347. if (!checkType) return;
  348. const resp = await checkRequest({
  349. computeDate: currentComputeDate as string,
  350. auditType: type == '0' ? '1' : '0', //审核类型 1审核 0取消审核
  351. reportCode: checkType == '2' ? currentTabKey : undefined
  352. }, checkType);
  353. if (resp) {
  354. if (type == '0') {
  355. message.success('审核提交成功!');
  356. set_auditType('1');
  357. }
  358. if (type == '1') {
  359. message.success('取消审核提交成功!');
  360. set_auditType('0');
  361. }
  362. }
  363. return resp;
  364. }
  365. const getCurrentComputeDate = async () => {
  366. const resp = await getComputeDate();
  367. set_currentComputeDate(resp);
  368. }
  369. const getTabList = async () => {
  370. const resp = await getZhileiList();
  371. if (resp) {
  372. set_Tabs(resp.list);
  373. }
  374. }
  375. const confirmCaculateHandle = async () => {
  376. const resp = await caculate(currentComputeDate as string);
  377. if (resp) {
  378. tableRef.current?.reload();
  379. }
  380. }
  381. const getCheckStatus = async (computeDate: string, code?: string) => {
  382. if (checkType == '1') {
  383. const resp = await getCurrentCheckStatus(computeDate);
  384. if (resp) {
  385. set_auditType('1'); //0 未审核 1 已审核
  386. } else {
  387. set_auditType('0');
  388. }
  389. }
  390. if (checkType == '2') {
  391. const resp = await getCurrentZhileiCheckStatus(computeDate, code ? code : currentTabKey);
  392. if (resp) {
  393. set_auditType('1'); //0 未审核 1 已审核
  394. } else {
  395. set_auditType('0');
  396. }
  397. }
  398. }
  399. const generateFunc = () => {
  400. Modal.confirm({
  401. title: '注意',
  402. okText: '确定',
  403. cancelText: '',
  404. closable: true,
  405. content: '计算会覆盖原有数据,确定要继续计算操作?',
  406. onOk: () => confirmCaculateHandle()
  407. })
  408. }
  409. const exportHandle = () => {
  410. exportTableToMultiExcel(tableData, columnsForExcel as any, reportTitle, true);
  411. }
  412. const handleResize = (e: any) => {
  413. const wH = e.target.innerHeight;
  414. const tableHeight = wH - 290;
  415. set_tableH(tableHeight);
  416. }
  417. const getCheckTypeHandle = async () => {
  418. const resp = await getCheckType();
  419. if (resp && resp.list.length > 0) {
  420. set_checkType(resp.list[0].value);
  421. }
  422. }
  423. function doResize() {
  424. setTimeout(() => {
  425. const ev: any = new Event('resize');
  426. window.dispatchEvent(ev);
  427. }, 0)
  428. }
  429. useEffect(() => {
  430. if (tabs.length > 0) {
  431. set_currentTabKey(tabs[0].value);
  432. }
  433. }, [tabs]);
  434. useEffect(() => {
  435. if (currentComputeDate && checkType == '2') {
  436. getCheckStatus(currentComputeDate as string);
  437. }
  438. }, [currentTabKey]);
  439. useEffect(() => {
  440. if (checkType == '2') {
  441. checkStatusArr = [];
  442. tabs.forEach(async (item) => {
  443. const resp = await getCurrentZhileiCheckStatus(currentComputeDate as string, item.value);
  444. checkStatusArr.push(resp);
  445. if (checkStatusArr.length == tabs.length) {
  446. const total = [...checkStatusArr].reduce((prev, cur) => prev + cur, 0);
  447. total > 0 ? set_disAccount(true) : set_disAccount(false)
  448. }
  449. })
  450. }
  451. }, [auditType]);
  452. const getJiezhuanStatusHandle = async () => {
  453. const resp = await getJiezhuanStatus(currentComputeDate as string);
  454. if (resp == 2) {
  455. set_ifBanAllAction(true);
  456. } else {
  457. set_ifBanAllAction(false);
  458. }
  459. }
  460. useEffect(() => {
  461. if (currentComputeDate && checkType) {
  462. getCheckStatus(currentComputeDate);
  463. getJiezhuanStatusHandle();
  464. }
  465. }, [currentComputeDate, checkType]);
  466. useEffect(() => {
  467. getCurrentComputeDate();
  468. window.addEventListener('resize', (e) => handleResize(e)) //监听窗口大小改变
  469. doResize();
  470. getTabList();
  471. getCheckTypeHandle();
  472. return () => {
  473. window.removeEventListener('resize', (e) => handleResize(e))
  474. }
  475. }, [])
  476. return (
  477. <BMSPagecontainer className='OneBatch' title={`核算年月:${currentComputeDate}`}>
  478. <div className='btnGroup'>
  479. {
  480. !ifBanAllAction && (
  481. <Popover open={ifShowTip} content={'当前处于审核中,无法操作!'} >
  482. <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
  483. <div className={(auditType == '0' && !disAccount) ? 'caculateBtn' : 'caculateBtn disabled'}
  484. onMouseEnter={() => auditType == '0' ? set_ifShowTip(false) : set_ifShowTip(true)}
  485. onMouseLeave={() => set_ifShowTip(false)}
  486. onClick={() => (auditType == '0' && !disAccount) ? generateFunc() : () => { }}>计算</div>
  487. <div className={auditType == '0' ? 'caculateBtn' : 'caculateBtn disabled'}
  488. onMouseEnter={() => auditType == '0' ? set_ifShowTip(false) : set_ifShowTip(true)}
  489. onMouseLeave={() => set_ifShowTip(false)}
  490. onClick={() => auditType == '0' ? exportHandle() : () => { }}>导出</div>
  491. </div>
  492. </Popover>
  493. )
  494. }
  495. {!ifBanAllAction && <div className='checkBtn' onClick={() => checkHandle(`${auditType}`)}>{auditType == '0' ? '审核' : '取消审核'}</div>}
  496. </div>
  497. <div className='content'>
  498. <Tabs
  499. defaultActiveKey='1'
  500. onChange={onTabChange}
  501. items={[
  502. ...tabs.map(a => ({ label: a.name, key: a.value }))
  503. ]}
  504. />
  505. <div className='tabContent'>
  506. {currentComputeDate && <BMSTable actionRef={tableRef} rowKey='unitCode' bordered pagination={false} columns={tableColumn as ProColumns[]}
  507. params={tableDataFilterParams}
  508. scroll={{ x: 140 * tableColumn.length, y: tableH }}
  509. request={(params, sort, filter) => getTableData(params, sort, filter)}
  510. summary={(pageData) => {
  511. const Caculate = ({ colData }: { colData: any }) => {
  512. const { dataIndex, children } = colData;
  513. if (children) {
  514. return children.map((child: any, index: number) => (
  515. <Caculate key={index} colData={child} />
  516. ));
  517. }
  518. const total = pageData.reduce((prev, cur) => {
  519. //return prev + cur[`${dataIndex}`]
  520. if (typeof cur[`${dataIndex}`] == 'number') {
  521. return math.add(prev, cur[`${dataIndex}`] as number);
  522. }
  523. }, 0);
  524. return dataIndex == "unitName" ? <div style={{ textAlign: 'left' }}>合计</div> : (
  525. //<span>{typeof total == 'number'?math.format(total, {precision: 14}) : '合计'}</span>
  526. <span style={{ textAlign: 'left' }}>{typeof total == 'number' ? Number(total.toFixed(4)) : '-'}</span>
  527. )
  528. }
  529. const renderSummaryRow = (columns: any[]): React.ReactNode[] => {
  530. return columns.map((colData, index) => {
  531. if (colData.children) {
  532. return renderSummaryRow(colData.children);
  533. } else {
  534. return (
  535. <Table.Summary.Cell key={index} index={index} align='center' rowSpan={1}>
  536. <Caculate colData={colData} />
  537. </Table.Summary.Cell>
  538. );
  539. }
  540. });
  541. };
  542. return (
  543. <Table.Summary fixed>
  544. <Table.Summary.Row>
  545. {renderSummaryRow(tableColumn)}
  546. </Table.Summary.Row>
  547. </Table.Summary>
  548. );
  549. }}
  550. />}
  551. </div>
  552. </div>
  553. </BMSPagecontainer>
  554. )
  555. }
  556. export default OneBatch