index.tsx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. /*
  2. * @Author: code4eat awesomedema@gmail.com
  3. * @Date: 2023-03-03 11:30:33
  4. * @LastEditors: code4eat awesomedema@gmail.com
  5. * @LastEditTime: 2025-05-12 14:59:43
  6. * @FilePath: /KC-MiddlePlatform/src/pages/platform/setting/pubDicTypeMana/index.tsx
  7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
  8. */
  9. import KCIMPagecontainer from '@/components/KCIMPageContainer';
  10. import { KCIMTable } from '@/components/KCIMTable';
  11. import { createFromIconfontCN } from '@ant-design/icons';
  12. import { ActionType, ProFormDependency, ProFormInstance, ProFormText, ProFormSelect, ProFormDigit, ProFormRadio } from '@ant-design/pro-components';
  13. import { ModalForm, ProFormTextArea } from '@ant-design/pro-form'
  14. import { ProColumns } from '@ant-design/pro-table';
  15. import { Modal, message, Popconfirm, Form, Tabs, Switch, Row, Col } from 'antd';
  16. import { Key, useEffect, useRef, useState } from 'react';
  17. import 'moment/locale/zh-cn';
  18. import locale from 'antd/es/date-picker/locale/zh_CN';
  19. import { addData, copyDataToSelectedType, delData, editData, getReportProjectSettingList, saveReportRelation } from './service';
  20. import './style.less';
  21. import TableTransfer from './transform';
  22. import React from 'react';
  23. import { cleanTree } from '@/utils/tooljs';
  24. import { getDicDataBySysId } from '@/services/getDic';
  25. const IconFont = createFromIconfontCN({
  26. scriptUrl: '',
  27. });
  28. let currentRow: any = undefined;
  29. const accountingSubjectColumn = [
  30. {
  31. title: '会计科目编码',
  32. dataIndex: 'accountingCode',
  33. ellipsis: true,
  34. },
  35. {
  36. title: '会计科目名',
  37. dataIndex: 'accountingName',
  38. ellipsis: true,
  39. },
  40. ];
  41. const shareParamsColumn = [
  42. {
  43. title: '分摊层级Id',
  44. dataIndex: 'id',
  45. ellipsis: true,
  46. },
  47. {
  48. title: '分摊层级名',
  49. dataIndex: 'shareName',
  50. ellipsis: true,
  51. },
  52. ];
  53. const responsibilityColumn = [
  54. {
  55. title: '责任中心编码',
  56. dataIndex: 'responsibilityCode',
  57. ellipsis: true,
  58. },
  59. {
  60. title: '责任中心名',
  61. dataIndex: 'responsibilityName',
  62. ellipsis: true,
  63. },
  64. ];
  65. const openRule = () => {
  66. Modal.info({
  67. title: '报表项目填写注意事项!',
  68. width: 800,
  69. content: (
  70. <div>
  71. <h4>计算类型说明 </h4>
  72. <span>不设置:</span>
  73. 一般用于第一层。 对应成本科目:对应的是成本科目,如工资,收入类等
  74. <div></div>
  75. <span>对应责任中心:</span>
  76. 对应的是分摊的责任中心,放射科,功能科等
  77. <div></div>
  78. <span>对应分摊层级:</span>
  79. 对应的是分摊的层级,如"护理"分摊层级,"医技"分摊层级。
  80. <div></div>
  81. <span>小计:</span>
  82. 是对第一层下的所有项目的合计计算。
  83. <div></div>
  84. <span>计算公式:</span>
  85. 是按编号对报表项目的项目进行计算结果,支持加/减法。
  86. <div style={{ marginBottom: 20 }}></div>
  87. <h4>报表项目顺序注意事项</h4>
  88. 计算类型是小计报表项目需要维护在对应层级的其他项目之后
  89. <div></div>
  90. 计算类型是计算公式的报表项目需要维护在公式里包含的其他项目之后
  91. </div>
  92. ),
  93. });
  94. };
  95. export default function ReportItemSet() {
  96. const [tableDataFilterParams, set_tableDataFilterParams] = useState<any | undefined>({ reportType: 0 });
  97. const tableRef = useRef<ActionType>();
  98. const formRef = useRef<ProFormInstance>();
  99. const [tabs, set_tabs] = useState<any[]>([]);
  100. const [reportType, set_reportType] = useState<Key | undefined>(undefined);
  101. const [calcTypes, setCalcTypes] = useState<any[]>([]);
  102. const columns: ProColumns[] = [
  103. {
  104. title: '报表项目名',
  105. dataIndex: 'reportName',
  106. width: 200
  107. },
  108. {
  109. title: 'Id',
  110. dataIndex: 'id',
  111. width: 80
  112. },
  113. {
  114. title: '编号',
  115. dataIndex: 'num',
  116. width: 80
  117. },
  118. {
  119. title: '计算方式',
  120. dataIndex: 'calcType',
  121. renderText: (num, record) => {
  122. const temp = calcTypes.filter((a) => a.code == num);
  123. return temp.length > 0 ? temp[0].name : ''
  124. },
  125. },
  126. // {
  127. // title: '报表类型',
  128. // dataIndex: 'reportType',
  129. // renderText(num) {
  130. // switch (num) {
  131. // case 0:
  132. // return <>损益表</>;
  133. // case 1:
  134. // return <>完全成本法表</>;
  135. // case 2:
  136. // return <>变动成本表</>;
  137. // case 3:
  138. // return <>全院损益表</>;
  139. // case 4:
  140. // return <>全成本报表</>;
  141. // }
  142. // },
  143. // },
  144. {
  145. title: '计算公式',
  146. width: 300,
  147. dataIndex: 'calcFormula',
  148. },
  149. {
  150. title: '项目类型',
  151. dataIndex: 'costType',
  152. renderText(num) {
  153. switch (num) {
  154. case 1:
  155. return <>收入</>;
  156. case 2:
  157. return <>成本</>;
  158. }
  159. },
  160. },
  161. {
  162. title: '占比计算类型',
  163. dataIndex: 'fraction',
  164. renderText(num) {
  165. switch (num) {
  166. case 1:
  167. return <>分子</>;
  168. case 2:
  169. return <>分母</>;
  170. case 3:
  171. return <>不计算</>;
  172. }
  173. },
  174. },
  175. {
  176. title: '排序',
  177. dataIndex: 'sort',
  178. width: 80
  179. },
  180. {
  181. title: '说明',
  182. dataIndex: 'description',
  183. ellipsis: true
  184. },
  185. {
  186. title: '显示',
  187. fixed: 'right',
  188. width: 60,
  189. dataIndex: 'hide',
  190. renderText: (_: any, record: any) => {
  191. return <Switch size='small' checked={_ == 1} onChange={(bool) => updateTable({ ...record, hide: bool ? 1 : 0 }, 'EDIT')} />
  192. }
  193. },
  194. {
  195. title: '操作',
  196. key: 'option',
  197. width: 200,
  198. fixed: 'right',
  199. valueType: 'option',
  200. render: (_: any, record: any) => {
  201. const { showAddRelation, parentId, calcType } = record;
  202. const btnGroup = true ? [
  203. <UpDataActBtn key={'addChild'} record={record} type='ADDCHILD' />,
  204. <UpDataActBtn key={'act'} record={record} type='EDIT' />,
  205. <Popconfirm
  206. title="是否确认删除?"
  207. key="del"
  208. onConfirm={() => delTableData(record)}
  209. >
  210. <a>删除</a>
  211. </Popconfirm>,
  212. ] : [
  213. <UpDataActBtn key={'act'} record={record} type='EDIT' />,
  214. <Popconfirm
  215. title="是否确认删除?"
  216. key="del"
  217. onConfirm={() => delTableData(record)}
  218. >
  219. <a>删除</a>
  220. </Popconfirm>,
  221. ]
  222. return showAddRelation ? [
  223. ...btnGroup,
  224. <a key="config3" onClick={() => {
  225. currentRow = record;
  226. if (showAddRelation == 1) oprnTableTransform(record, accountingSubjectColumn, 1);
  227. if (showAddRelation == 2) oprnTableTransform(record, shareParamsColumn, 2);
  228. if (showAddRelation == 3) oprnTableTransform(record, responsibilityColumn, 3);
  229. }}>
  230. {showAddRelation == 1
  231. ? '会计科目'
  232. : showAddRelation == 2
  233. ? '分摊层级'
  234. : showAddRelation == 3 && '责任中心'}
  235. </a>
  236. ] : btnGroup
  237. },
  238. },
  239. ]
  240. const getTableData = async (params: any) => {
  241. const { reportType } = params;
  242. if (reportType == undefined) return []
  243. const resp = await getReportProjectSettingList({ ...params });
  244. if (resp) {
  245. return {
  246. data: cleanTree(resp.list),
  247. success: true,
  248. total: resp.totalCount,
  249. pageSize: resp.pageSize,
  250. totalPage: resp.totalPage,
  251. }
  252. }
  253. return []
  254. }
  255. const delTableData = async (record: any) => {
  256. const resp = await delData([record.id]);
  257. if (resp) {
  258. message.success('操作成功!');
  259. tableRef.current?.reload();
  260. // message.success('操作成功!');
  261. }
  262. }
  263. const onSaveHandle = async (keys: Key[], rows: any[], record: any, settingType: number) => {
  264. let selectedRowKeysToStr: string[] = [];
  265. if (settingType == 2) {
  266. keys
  267. ? keys.map((item: any) => selectedRowKeysToStr.push(`${item}`))
  268. : [];
  269. }
  270. let selectedRowKeys: Key[] = [];
  271. if (settingType == 1) {
  272. selectedRowKeys = rows.map((a: any) => a.accountingCode)
  273. }
  274. if (settingType == 2) {
  275. selectedRowKeys = keys.map((a: any) => `${a}`)
  276. }
  277. if (settingType == 3) {
  278. selectedRowKeys = rows.map((a: any) => a.responsibilityCode)
  279. }
  280. const { id: reportId } = record;
  281. const resp = await saveReportRelation({
  282. reportId,
  283. reportType,
  284. relation: settingType,
  285. relationCodes: selectedRowKeys,
  286. });
  287. if (resp) {
  288. message.success('对照成功!');
  289. tableRef.current?.reload();
  290. }
  291. }
  292. const oprnTableTransform = (record: any, transferTableColumn: ProColumns[], settingType: number) => {
  293. const ref = React.createRef<{ save: any; }>();
  294. Modal.confirm({
  295. title: settingType == 1 ? `选择会计科目` : settingType == 2 ? '选择分摊层级' : '选择责任中心',
  296. icon: <></>,
  297. width: 1000,
  298. centered: true,
  299. okText: '确定',
  300. cancelText: '取消',
  301. content: <TableTransfer
  302. ref={ref}
  303. keyName={settingType == 1 ? `accountingCode` : settingType == 2 ? 'id' : 'responsibilityCode'}
  304. record={record}
  305. settingType={settingType}
  306. onSave={(keys, rows, settingType) => onSaveHandle(keys, rows, record, settingType)}
  307. leftColumns={transferTableColumn}
  308. rightColumns={transferTableColumn}
  309. ></TableTransfer>,
  310. onOk: () => {
  311. return ref.current && ref.current.save();
  312. }
  313. })
  314. }
  315. const updateTable = async (formVal: any, type: 'EDIT' | "ADD" | "ADDCHILD" | 'COPY') => {
  316. if (type == 'ADD' || type == 'ADDCHILD') {
  317. let parentId = 0;
  318. const { calcType = 0, calcFormula = '' } = formVal;
  319. (type == 'ADDCHILD') && (parentId = formVal.id);
  320. const result = {
  321. ...formVal,
  322. calcType,
  323. calcFormula,
  324. isLoss: formVal.isLoss ? 1 : 0,
  325. reportType: Number(reportType),
  326. parentId,
  327. decimalPlace:formVal.decimalPlace,
  328. permil: formVal.dataType === 1 ? (formVal.permil ? 1 : 0) : 0,
  329. }
  330. const resp = await addData(result);
  331. if (resp) {
  332. currentRow = undefined;
  333. tableRef.current?.reload();
  334. message.success('操作成功!');
  335. }
  336. }
  337. if (type == 'EDIT') {
  338. try {
  339. const { reportName, sort, calcType = 0, calcFormula = '', isLoss, parentId, id, fraction, costType, description, hide, dataType, decimalPlace, permil } = formVal;
  340. const result = {
  341. id,
  342. parentId,
  343. reportName,
  344. sort,
  345. reportType,
  346. calcType,
  347. calcFormula,
  348. fraction,
  349. costType,
  350. isLoss: isLoss ? 1 : 0,
  351. description,
  352. hide,
  353. dataType,
  354. decimalPlace:decimalPlace,
  355. permil: dataType === 1 ? (permil ? 1 : 0) : 0,
  356. };
  357. const resp = await editData(result);
  358. if (resp) {
  359. tableRef.current?.reload();
  360. message.success('操作成功!');
  361. }
  362. } catch (error) {
  363. console.log({ error });
  364. }
  365. }
  366. if (type == 'COPY') {
  367. //复制数据
  368. const { toReportType } = formVal;
  369. const resp = await copyDataToSelectedType({
  370. fromReportType: Number(reportType),
  371. toReportType,
  372. });
  373. if (resp) {
  374. message.success('复制成功!');
  375. tableRef.current?.reload();
  376. }
  377. }
  378. return true;
  379. }
  380. const UpDataActBtn = ({ record, type }: { record: any, type: 'EDIT' | 'ADD' | 'ADDCHILD' | 'COPY' }) => {
  381. const reportType = type == 'EDIT' || type == 'ADDCHILD' ? record.reportType : undefined
  382. return (
  383. <ModalForm
  384. title={type == 'COPY' ? '复制' : `${type == 'EDIT' ? '编辑' : '新增'}报表`}
  385. width={380}
  386. formRef={formRef}
  387. initialValues={type == 'EDIT' ? {
  388. ...record,
  389. permil: record.dataType === 1 ? !!record.permil : false,
  390. } : { dataType: 1, decimalPlaces: 2, permil: false }}
  391. trigger={
  392. type == 'EDIT' ? <a key="edit" >编辑</a> : type == 'ADDCHILD' ? <a className='add'>添加</a> : type == 'COPY' ? <span className='copy' style={{ marginRight: 8 }}>复制</span> : <span className='add'>新增</span>
  393. }
  394. onFinish={(val) => {
  395. return updateTable(type == 'EDIT' ? { ...record, ...val } : type == 'ADDCHILD' ? { ...val, id: record.id } : { ...val }, type);
  396. }}
  397. modalProps={{ destroyOnClose: true }}
  398. colProps={{ span: 24 }}
  399. grid
  400. >
  401. {
  402. type != 'COPY' && (
  403. <>
  404. <ProFormText
  405. label="报表名"
  406. rules={[
  407. {
  408. required: true,
  409. message: '报表名是必填项!',
  410. },
  411. ]}
  412. name="reportName"
  413. />
  414. <ProFormDigit
  415. label="排序"
  416. rules={[
  417. {
  418. required: true,
  419. message: '排序必填项!',
  420. },
  421. ]}
  422. name="sort"
  423. />
  424. {(
  425. //新增顶级不展示
  426. <>
  427. <ProFormSelect
  428. name="calcType"
  429. label="计算类型"
  430. options={[
  431. ...(calcTypes.map((a) => ({ label: a.name, value: Number(a.code) })))
  432. ]}
  433. placeholder="请选择状态"
  434. rules={[{ required: true, message: '请选择计算类型!' }]}
  435. />
  436. <ProFormDependency name={['calcType']}>
  437. {({ calcType }) => {
  438. // const resp = await getResponsibilityCenterList({pageSize:100,current:1});
  439. return calcType == 4 ? (
  440. <ProFormText
  441. label="计算公式"
  442. rules={[
  443. {
  444. required: true,
  445. message: '计算公式是必填项!',
  446. },
  447. ]}
  448. placeholder="例如:[1] + [2]"
  449. name="calcFormula"
  450. />
  451. ) : (
  452. <></>
  453. );
  454. }}
  455. </ProFormDependency>
  456. <ProFormDependency name={['calcType']}>
  457. {({ calcType }) => {
  458. // const resp = await getResponsibilityCenterList({pageSize:100,current:1});
  459. return calcType == 4 && reportType == 3 ? (
  460. <Form.Item
  461. name="isLoss"
  462. label="损益标志"
  463. valuePropName="checked"
  464. >
  465. <Switch size='small' />
  466. </Form.Item>
  467. ) : (
  468. <></>
  469. );
  470. }}
  471. </ProFormDependency>
  472. </>
  473. )}
  474. <ProFormSelect
  475. name="costType"
  476. label="报表项目类型"
  477. options={[
  478. { label: '收入', value: 1 },
  479. { label: '成本', value: 2 },
  480. ]}
  481. placeholder="请选择"
  482. rules={[{ required: true, message: '请选择报表项目类型!' }]}
  483. />
  484. <ProFormSelect
  485. name="fraction"
  486. label="占比计算类型"
  487. options={[
  488. { label: '分子', value: 1 },
  489. { label: '分母', value: 2 },
  490. { label: '不计算', value: 3 },
  491. ]}
  492. placeholder="请选择"
  493. rules={[{ required: true, message: '请选择占比计算类型!' }]}
  494. />
  495. <ProFormRadio.Group
  496. name="dataType"
  497. label="数据格式"
  498. options={[
  499. { label: '数值', value: 1 },
  500. { label: '百分比', value: 2 },
  501. ]}
  502. rules={[{ required: true }]}
  503. />
  504. <ProFormDependency name={['dataType']}>
  505. {({ dataType }) => {
  506. return dataType === 1 ? (
  507. <>
  508. <Row gutter={16}>
  509. <Col span={12}>
  510. <ProFormDigit
  511. label="小数位数"
  512. name="decimalPlace"
  513. min={0}
  514. max={10}
  515. fieldProps={{ precision: 0 }}
  516. rules={[{ required: true, message: '请输入小数位数!' }]}
  517. />
  518. </Col>
  519. <Col span={12}>
  520. <Form.Item
  521. label="是否需要千分号"
  522. name="permil"
  523. valuePropName="checked"
  524. style={{ marginBottom: '24px' }}
  525. >
  526. <Switch size='small' />
  527. </Form.Item>
  528. </Col>
  529. </Row>
  530. </>
  531. ) : <ProFormDigit
  532. label="小数位数"
  533. name="decimalPlace"
  534. min={0}
  535. max={10}
  536. fieldProps={{ precision: 0 }}
  537. rules={[{ required: true, message: '请输入小数位数!' }]}
  538. />;
  539. }}
  540. </ProFormDependency>
  541. <ProFormTextArea
  542. label="说明"
  543. placeholder='请输入'
  544. name="description"
  545. />
  546. </>
  547. )
  548. }
  549. {
  550. type == 'COPY' && (
  551. <ProFormSelect
  552. name="toReportType"
  553. label="复制到"
  554. width="sm"
  555. options={tabs.map((a) => ({ label: a.label, value: a.key }))}
  556. placeholder="请选择"
  557. rules={[{ required: true, message: '请选择复制到目标!' }]}
  558. />
  559. )
  560. }
  561. </ModalForm>
  562. )
  563. }
  564. const onTabChanged = (key: Key) => {
  565. set_reportType(key);
  566. set_tableDataFilterParams({ ...tableDataFilterParams, reportType: key })
  567. }
  568. const getTabs = async () => {
  569. const { systemId } = JSON.parse((localStorage.getItem('currentSelectedTab')) as string)
  570. const resp = await getDicDataBySysId(systemId, 'PROFIT_REPORT_TYPE');
  571. if (resp) {
  572. const { dataVoList } = resp;
  573. set_tabs(dataVoList.map((a: any) => ({ label: a.name, key: Number(a.code) })));
  574. set_tableDataFilterParams({ ...tableDataFilterParams, reportType: dataVoList.length > 0 ? dataVoList[0].code : undefined });
  575. set_reportType(dataVoList.length > 0 ? dataVoList[0].code : undefined);
  576. }
  577. }
  578. useEffect(() => {
  579. const fetchData = async () => {
  580. const { systemId } = JSON.parse(localStorage.getItem('currentSelectedTab') as string);
  581. const resp = await getDicDataBySysId(systemId, 'REPORT_ITEM_CALC_TYPE');
  582. setCalcTypes(resp?.dataVoList ?? []);
  583. };
  584. fetchData();
  585. getTabs();
  586. }, [])
  587. return (
  588. <KCIMPagecontainer className='ReportItemSet' title={false}>
  589. <div className='header'>
  590. <div className='pageTitle'>报表项目设置</div>
  591. <div className='btnGroup'>
  592. <span style={{ marginRight: 8, cursor: 'pointer' }} onClick={() => openRule()}><img style={{ width: 16, marginRight: 4, marginTop: -1 }} src={require('../../../../../static/tip.png')} />说明</span>
  593. <UpDataActBtn record={undefined} type='COPY' />
  594. <UpDataActBtn record={undefined} type='ADD' />
  595. </div>
  596. </div>
  597. <div className='toolBar' style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
  598. <Tabs
  599. defaultActiveKey={tabs.length > 0 ? tabs[0].key : undefined}
  600. items={tabs}
  601. key={'key'}
  602. onChange={(key) => onTabChanged(key)}
  603. />
  604. </div>
  605. <div>
  606. <KCIMTable pagination={false} scroll={{ x: 1700, y: `calc(100vh - 191px)` }} columns={columns as ProColumns[]} actionRef={tableRef} rowKey='id' params={tableDataFilterParams} request={(params) => getTableData(params)} />
  607. </div>
  608. </KCIMPagecontainer>
  609. )
  610. }