detail.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. import PageContent from '@/components/PageContent';
  2. import { Button, Tabs, Space, Drawer, Timeline, message } from 'antd';
  3. import styles from './index.less';
  4. import { useEffect, useMemo, useRef, useState } from 'react';
  5. import axios from 'axios';
  6. import {
  7. useLocation,
  8. useParams,
  9. useRequest,
  10. useSearchParams,
  11. useNavigate,
  12. } from '@umijs/max';
  13. import {
  14. queryPsrExcel,
  15. queryPsrMonthDetail,
  16. queryPsrMonthLast,
  17. queryPsrMonthList,
  18. queryPsrWorkLoad,
  19. querySavePsrMonth,
  20. } from '../../services/psr';
  21. import SaveModal from './components/saveOtherModal';
  22. import CompareModal from './components/compareModal';
  23. import CompareCom from './components/compareCom';
  24. import dayjs from 'dayjs';
  25. import { getToken } from '@/utils/utils';
  26. import { exportExcel, getUUID } from '../../utils/exportExcl';
  27. import { stringify } from 'qs';
  28. const PSRDetail = () => {
  29. const navigate = useNavigate();
  30. const params = useParams();
  31. const location = useLocation();
  32. const token = getToken();
  33. const { id: projectId } = params;
  34. const {
  35. state: { project_name, flow_id, node_id },
  36. } = location;
  37. const [excelData, setExcelData] = useState();
  38. const [historyOpen, setHistoryOpen] = useState();
  39. const [open, setOpen] = useState(false);
  40. const [key, setKey] = useState();
  41. const [compareOpen, setCompareOpen] = useState(false);
  42. const [compareValues, setCompareValues] = useState([]);
  43. const [isOriginVer, setIsOriginVer] = useState(false); //是否原始版本 是的话只能另存不能编辑
  44. const luckysheetRef = useRef();
  45. const iframeRef = useRef();
  46. const unableEdit = (option) => {
  47. option.showtoolbar = false;
  48. option.enableAddRow = false;
  49. option.sheetFormulaBar = false;
  50. option.enableAddBackTop = false;
  51. option.showsheetbarConfig = {
  52. add: false,
  53. sheet: false,
  54. };
  55. option.cellRightClickConfig = {
  56. copy: false, // 复制
  57. copyAs: false, // 复制为
  58. paste: false, // 粘贴
  59. insertRow: false, // 插入行
  60. insertColumn: false, // 插入列
  61. deleteRow: false, // 删除选中行
  62. deleteColumn: false, // 删除选中列
  63. deleteCell: false, // 删除单元格
  64. hideRow: false, // 隐藏选中行和显示选中行
  65. hideColumn: false, // 隐藏选中列和显示选中列
  66. rowHeight: false, // 行高
  67. columnWidth: false, // 列宽
  68. clear: false, // 清除内容
  69. matrix: false, // 矩阵操作选区
  70. sort: false, // 排序选区
  71. filter: false, // 筛选选区
  72. chart: false, // 图表生成
  73. image: false, // 插入图片
  74. link: false, // 插入链接
  75. data: false, // 数据验证
  76. cellFormat: false, // 设置单元格格式
  77. };
  78. };
  79. //请求投标版、签字版psr excel
  80. // const { run: runExcel } = useRequest(queryPsrExcel, {
  81. // manual: true,
  82. // formatResult: (res) => {
  83. // if (res) {
  84. // const jsonData = JSON.parse(res);
  85. // renderSheet(jsonData);
  86. // }
  87. // },
  88. // });
  89. //请求月度psr和现金流列表 data_type 1 psr 2 现金流
  90. const {
  91. data,
  92. run: runList,
  93. loading: listLoading,
  94. } = useRequest(
  95. (data) =>
  96. queryPsrMonthList({
  97. project_id: projectId,
  98. data_type: data_type,
  99. ...data,
  100. }),
  101. { manual: true },
  102. );
  103. //保存/另存为月度psr和现金流接口
  104. const { run: runSave, loading: saveLoading } = useRequest(
  105. (data) => querySavePsrMonth(data),
  106. {
  107. manual: true,
  108. onSuccess: () => {
  109. message.success('保存成功');
  110. if (open) setOpen(false);
  111. },
  112. onError: () => {
  113. message.success('添加失败');
  114. },
  115. },
  116. );
  117. //请求月度psr和现金流详情接口
  118. const { run: runDetail } = useRequest((data) => queryPsrMonthDetail(data), {
  119. manual: true,
  120. formatResult: (res) => {
  121. if (res?.data) {
  122. let data = res.data;
  123. console.log(data);
  124. const day = dayjs(data.day).format('YYYY-MM');
  125. setExcelData({ ...data, dayFormat: day });
  126. setHistoryOpen(false);
  127. const jsonData = JSON.parse(data.json_data);
  128. renderSheet(jsonData, data.is_edit);
  129. }
  130. },
  131. });
  132. const data_type = useMemo(() => {
  133. if (key == '3') return 1;
  134. if (key == '4') return 2;
  135. return null;
  136. }, [key]);
  137. useEffect(() => {
  138. setCompareValues([]);
  139. if (key) {
  140. onChange(key);
  141. } else {
  142. queryPsrExcel({ gridKey: node_id });
  143. }
  144. }, [key]);
  145. //请求投标版、签字版psr excel
  146. const queryPsrExcel = (data) => {
  147. axios({
  148. url: `/api/v1/purchase/record/sheet?${stringify(data)}`,
  149. method: 'POST',
  150. data,
  151. headers: {
  152. 'JWT-TOKEN': token,
  153. },
  154. }).then((req) => {
  155. if (req.status == 200) {
  156. const jsonData = JSON.parse(req.data);
  157. renderSheet(jsonData);
  158. }
  159. });
  160. };
  161. const onChange = (key) => {
  162. if (key == '1') {
  163. queryPsrExcel({ gridKey: node_id });
  164. } else if (key == '2') {
  165. queryPsrExcel({ gridKey: flow_id });
  166. } else {
  167. initPsrActrual(key);
  168. }
  169. };
  170. const initPsrActrual = async (key) => {
  171. const data_type = key == '3' ? 1 : 2;
  172. // runList({ data_type });
  173. const res = await queryPsrMonthLast({ project_id: projectId, data_type });
  174. if (res.data?.length > 0 && res.data[0].json_data) {
  175. const day = dayjs(res.data[0].day).format('YYYY-MM');
  176. const isOrigin = day.includes('1970-01') ? 1 : 0;
  177. setIsOriginVer(isOrigin);
  178. setExcelData({ ...res.data[0], dayFormat: day });
  179. const data = JSON.parse(res.data[0].json_data);
  180. console.log(data);
  181. if (data.celldata) data.celldata = JSON.parse(data.celldata);
  182. if (data.config) data.config = JSON.parse(data.config);
  183. renderSheet(
  184. Array.isArray(data) ? data : [data],
  185. isOrigin ? 0 : res.data[0].is_edit,
  186. );
  187. }
  188. };
  189. const handlerSaveOther = (date) => {
  190. const luckyData = luckysheetRef.current?.toJson();
  191. if (luckyData?.data) {
  192. const params = {
  193. day: date,
  194. project_id: Number(projectId),
  195. json_data: JSON.stringify(luckyData.data),
  196. data_type: data_type,
  197. };
  198. runSave(params);
  199. }
  200. };
  201. const handlerSave = () => {
  202. const luckyData = luckysheetRef.current?.toJson();
  203. const params = {
  204. id: excelData?.id,
  205. data_type: excelData?.data_type,
  206. project_id: excelData?.project_id,
  207. day: dayjs(excelData?.day).format('YYYY-MM-DD'),
  208. json_data: JSON.stringify(luckyData.data),
  209. };
  210. runSave(params);
  211. };
  212. const exportExcl = (title) => {
  213. const luckyData = luckysheetRef.current?.toJson();
  214. exportExcel(luckyData.data, title);
  215. };
  216. const handlerLoad = () => {
  217. const contentWindow = iframeRef.current.contentWindow;
  218. luckysheetRef.current = contentWindow.luckysheet;
  219. };
  220. const renderSheet = (currentData, is_edit = false) => {
  221. console.log('==================', luckysheetRef.current);
  222. if (!luckysheetRef.current) {
  223. setTimeout(() => {
  224. renderSheet(currentData, is_edit);
  225. }, 500);
  226. return;
  227. }
  228. const data = currentData;
  229. //设置单元格不可编辑
  230. data?.forEach((item) => {
  231. item.config.authority = is_edit
  232. ? null
  233. : {
  234. sheet: true,
  235. hintText: '当前excel不可编辑!',
  236. };
  237. });
  238. let option = {
  239. lang: 'zh',
  240. showinfobar: false,
  241. showstatisticBar: false,
  242. data: JSON.parse(JSON.stringify(data)),
  243. hook: {
  244. cellMousedown: (cell, position, sheet) => {
  245. console.log(cell, sheet);
  246. },
  247. cellUpdated: () => {
  248. luckysheetRef.current.refreshFormula();
  249. },
  250. workbookCreateAfter: async () => {
  251. //当前为为终版psr标签并且可编辑状态时填充人日数据
  252. if (key == '3' && is_edit) {
  253. const res = await queryPsrWorkLoad({ project_id: projectId });
  254. if (res.data) {
  255. luckysheetRef.current.setCellValue(8, 4, res.data.Total);
  256. luckysheetRef.current.setCellValue(8, 8, res.data.Month);
  257. }
  258. }
  259. },
  260. },
  261. };
  262. option.data.forEach((item) => {
  263. delete item.data;
  264. if (item.celldata) {
  265. item.celldata.forEach((cell) => {
  266. // 生成uuid
  267. if (!cell.v.cid) cell.v.cid = getUUID();
  268. });
  269. }
  270. // 默认禁止编辑
  271. // item.config.authority = { sheet: true, hintText };
  272. });
  273. console.log(option.data);
  274. //设置不可编辑
  275. if (!is_edit) unableEdit(option);
  276. luckysheetRef.current.destroy();
  277. luckysheetRef.current.create(option);
  278. };
  279. const renderTitle = (data_type, title) => {
  280. if (compareValues?.length == 2) {
  281. return (
  282. <div className={styles.exportDiv}>
  283. <Button
  284. type="primary"
  285. onClick={() => {
  286. setCompareValues([]);
  287. setTimeout(() => {
  288. initPsrActrual(key);
  289. }, 500);
  290. }}
  291. >
  292. 退出比对
  293. </Button>
  294. </div>
  295. );
  296. }
  297. if (data_type == 0)
  298. return (
  299. <div className={styles.exportDiv}>
  300. <Button type="primary" onClick={() => exportExcl(title)}>
  301. 导出
  302. </Button>
  303. </div>
  304. );
  305. return (
  306. <div className={styles.excelTitle}>
  307. <div>
  308. 当前PSR表单:
  309. <span style={{ color: 'blue' }}>{excelData?.dayFormat}</span>
  310. </div>
  311. <Space>
  312. <Button
  313. type="primary"
  314. onClick={() => {
  315. runList();
  316. setHistoryOpen(true);
  317. }}
  318. >
  319. {`${title}历史版本`}
  320. </Button>
  321. <Button
  322. type="primary"
  323. onClick={() => setOpen(true)}
  324. disabled={!excelData?.is_edit}
  325. >
  326. 另存为
  327. </Button>
  328. <Button
  329. type="primary"
  330. onClick={handlerSave}
  331. disabled={!excelData?.is_edit || isOriginVer}
  332. >
  333. 保存
  334. </Button>
  335. <Button type="primary" onClick={() => setCompareOpen(true)}>
  336. 比对
  337. </Button>
  338. <Button type="primary" onClick={() => exportExcl(title)}>
  339. 导出
  340. </Button>
  341. </Space>
  342. </div>
  343. );
  344. };
  345. const items = [
  346. {
  347. key: '1',
  348. label: '投标版PSR',
  349. children: renderTitle(0, '投标版PSR'),
  350. },
  351. {
  352. key: '2',
  353. label: '签字版PSR',
  354. children: renderTitle(0, '签字版PSR'),
  355. },
  356. {
  357. key: '3',
  358. label: '终版PSR',
  359. children: renderTitle(1, '终版PSR'),
  360. },
  361. {
  362. key: '4',
  363. label: '现金流',
  364. children: renderTitle(2, '现金流'),
  365. },
  366. ];
  367. return (
  368. <PageContent>
  369. <div className={styles.titleDev}>
  370. <Button type="primary" onClick={() => navigate(-1)}>
  371. 返回
  372. </Button>
  373. <span className={styles.title}>{project_name}</span>
  374. </div>
  375. <Tabs defaultActiveKey="1" type="card" items={items} onChange={setKey} />
  376. {compareValues?.length == 2 ? (
  377. <CompareCom values={compareValues} />
  378. ) : (
  379. <iframe
  380. style={{
  381. width: '100%',
  382. height: '80vh',
  383. }}
  384. ref={iframeRef}
  385. onLoad={handlerLoad}
  386. src="/luckysheet.html"
  387. />
  388. )}
  389. <Drawer
  390. title="历史版本"
  391. placement="right"
  392. loading={listLoading}
  393. onClose={() => setHistoryOpen(false)}
  394. open={historyOpen}
  395. >
  396. <Timeline
  397. items={data?.list?.map((item) => {
  398. return {
  399. children: (
  400. <a onClick={() => runDetail({ id: item.id })}>
  401. {dayjs(item.day).format('YYYY-MM')}
  402. </a>
  403. ),
  404. };
  405. })}
  406. />
  407. </Drawer>
  408. <SaveModal
  409. loading={saveLoading}
  410. open={open}
  411. onCancel={() => setOpen(false)}
  412. onOk={handlerSaveOther}
  413. />
  414. <CompareModal
  415. list={data?.list}
  416. open={compareOpen}
  417. onOk={(values) => {
  418. setCompareValues(values);
  419. setCompareOpen(false);
  420. }}
  421. onCancel={() => setCompareOpen(false)}
  422. />
  423. </PageContent>
  424. );
  425. };
  426. export default PSRDetail;