Qingdan.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. import * as React from 'react';
  2. import Box from "@mui/material/Box";
  3. import 'handsontable/dist/handsontable.full.min.css';
  4. //import 'handsontable/styles/ht-theme-main.min.css';
  5. import { HandsonTable } from 'handsontable/base';
  6. import {HotTable} from "@handsontable/react";
  7. import { registerAllModules } from 'handsontable/registry';
  8. import {RichTreeView } from "@mui/x-tree-view/RichTreeView";
  9. import { Grid } from '@mui/material';
  10. import Tab from "@mui/material/Tab";
  11. import TabContext from "@mui/lab/TabContext";
  12. import TabList from "@mui/lab/TabList";
  13. import TabPanel from "@mui/lab/TabPanel";
  14. import Stack from "@mui/material/Stack";
  15. //import { registerPlugin, NestedRows } from 'handsontable/plugins';
  16. //registerPlugin(NestedRows);
  17. import Service from './Service';
  18. import { textRenderer, registerRenderer } from 'handsontable/renderers';
  19. import Button from '@mui/material/Button';
  20. import ButtonGroup from '@mui/material/ButtonGroup';
  21. import {danxiangdinge_index} from './utils';
  22. import {shanchu, undo, redo, quanbushanchu, danxiangdinge, updateDercj} from './editor';
  23. import { HyperFormula } from 'hyperformula';
  24. registerAllModules();
  25. /**
  26. *
  27. 本条规定了工程量清单编码的表示方式:十二位阿拉伯数字及其设置规定。
  28. 各位数字的含义是:一、二位为专业工程代码(01—房屋建筑与装饰工程;02—仿古建筑工程;
  29. 03—通用安装工程;04—市政工程;05—园林绿化工程;06—矿山工程;07—构筑物工程;08—城市
  30. 轨道交通工程;09—爆破工程。以后进入国标的专业工程代码以此类推);三、四位为附录分类顺序码;
  31. 五、六位为分部工程顺序码;七、八、九位为分项工程项目名称顺序码;十至十二位为清单项目名称
  32. 顺序码。
  33. */
  34. function copy(input) {
  35. return JSON.parse(JSON.stringify(input));
  36. }
  37. export default function Qingdan({name, bh, bt, rgde, jxde, clde, beizhu, clickCallback}) {
  38. const hyperformulaInstance = HyperFormula.buildEmpty({
  39. // to use an external HyperFormula instance,
  40. // initialize it with the `'internal-use-in-handsontable'` license key
  41. licenseKey: 'internal-use-in-handsontable',
  42. });
  43. function coverRenderer(_instance, td, _row, _col, _prop, value, _cellProperties) {
  44. let z = _instance.getDataAtRow(_row);
  45. if(z[1] == null || z[1].length == 0) {
  46. const button = document.createElement('button');
  47. button.innerText="删除";
  48. //img.src = value;
  49. /*button.addEventListener('click', (event) => {
  50. console.log(hotRef.current?.hotInstance?.getData()[selectedRow.current]);
  51. hotRef.current?.hotInstance?.alter('remove_row', selectedRow.current, 1);
  52. //console.log(plugin);
  53. event.preventDefault();
  54. });*/
  55. button.addEventListener('mousedown', (event) => {
  56. setDetail(shanchu(hotRef, selectedRow));
  57. //console.log(plugin);
  58. event.preventDefault();
  59. });
  60. td.innerText = '';
  61. td.appendChild(button);
  62. } else {
  63. const button = document.createElement('button');
  64. button.innerText="全部删除";
  65. //img.src = value;
  66. button.addEventListener('mousedown', (event) => {
  67. setDetail(quanbushanchu(hotRef, selectedRow));
  68. event.preventDefault();
  69. });
  70. td.innerText = '';
  71. td.appendChild(button);
  72. }
  73. return td;
  74. }
  75. registerRenderer('customStylesRenderer', (hotInstance, TD, row, column, ...rest) => {
  76. textRenderer(hotInstance, TD, row, column, ...rest);
  77. for (let i = 0; i < highlight.current.length; i++) {
  78. let entry = highlight.current[i];
  79. if (entry.row == row && entry.col == column) {
  80. TD.style.fontWeight = 'bold';
  81. TD.style.color = 'green';
  82. TD.style.background = '#d7f1e1';
  83. }
  84. }
  85. });
  86. registerRenderer('highlightRowRenderer', (hotInstance, TD, row, column, ...rest) => {
  87. if (row == selectedRow.current) {
  88. if(column > 0 ) {
  89. textRenderer(hotInstance, TD, row, column, ...rest);
  90. TD.style.color = 'green';
  91. TD.style.background = '#d7f1e1';
  92. }
  93. if (column == 0) {
  94. coverRenderer(hotInstance, TD, row, column, ...rest);
  95. }
  96. } else {
  97. textRenderer(hotInstance, TD, row, column, ...rest);
  98. }
  99. });
  100. const [detail, setDetail] = React.useState([
  101. {"序号": null, "清单编码" : null, "名称" : null,"项目特征" : null,
  102. "计算规则" : null,
  103. "单位" : null,
  104. "数量": null,
  105. "综合单价" : null,
  106. "合价" : null,
  107. "人工费": null,
  108. "主材费" : null,
  109. "设备费": null,
  110. "辅材费": null,
  111. "材料费" : null,
  112. "机械费" : null,
  113. "管理费": null,
  114. "利润": null,
  115. "暂估价" : null,
  116. "综合人工工日" : null,
  117. "备注" : null}]
  118. );
  119. const [value, setValue] = React.useState("1");
  120. const [rcjhl, setRcjhl] = React.useState([]);
  121. const [rcjhl2, setRcjhl2] = React.useState([]);
  122. const [fuzhu, setFuzhu] = React.useState([]);
  123. const [tuijian, setTuijian] = React.useState([]);
  124. const highlight = React.useRef([]);
  125. const selectedRow = React.useRef(-1);
  126. const hotRef = React.useRef(null);
  127. const hotRcjRef = React.useRef(null);
  128. const hotTuijianRef = React.useRef(null);
  129. const rgdeRef = React.useRef(null);
  130. const jxdeRef = React.useRef(null);
  131. const cldeRef = React.useRef(null);
  132. const isQdrcj = React.useRef(false);
  133. React.useEffect(
  134. () => {
  135. Service.generateQingdanmingxi(name, bh, bt).then(x=>{
  136. //console.log(x);
  137. //let y = x.map(t=>{
  138. // t["操作"]=`<div style="text-align:center"> <button>全部删除</button></div>`;
  139. // return t;
  140. //});
  141. setDetail(x);
  142. setRcjhl([]);
  143. selectedRow.current = -1;
  144. });
  145. }, [bh, bt]
  146. );
  147. React.useEffect(
  148. () => {
  149. //console.log(beizhu);
  150. let result = [["序号", "编号", "说明"]];
  151. if (beizhu != null) {
  152. let keys = Object.keys(beizhu["BZBH"]);
  153. for(let i = 0; i < keys.length; i++) {
  154. let key = keys[i];
  155. result.push([i+1, beizhu["BZBH"][key], beizhu["SM"][key]]);
  156. }
  157. setFuzhu(result);
  158. }
  159. }, [beizhu]
  160. );
  161. React.useEffect(
  162. () => {
  163. //console.log("rgde changed");
  164. //console.log(rgde);
  165. rgdeRef.current = rgde;
  166. jxdeRef.current = jxde;
  167. cldeRef.current = clde;
  168. if (isQdrcj.current) {
  169. highlight.current = [];
  170. } else {
  171. let toHighlight = [];
  172. for(let i = 1; i < rcjhl.length; i++) {
  173. let entry = rcjhl[i];
  174. let bianhao = entry[1];
  175. let rcjlb = entry[8];
  176. let hit = false;
  177. if (Number(rcjlb) == Number(1) && rgde) {
  178. for (let j = 0; j < rgde.length; j++) {
  179. if (rgde[j]["CLBH"] == bianhao) {
  180. if(rgde[j]["CLMC"] == entry[2])hit = true;
  181. let danjia = Number(entry[5]);
  182. if (danjia != rgde[j]["YSJG"]) {
  183. //console.log(`[${i},5]danjia bu yizhi`);
  184. toHighlight.push({row: i, col: 5, renderer: "customStylesRenderer"});
  185. }
  186. let hanliang = Number(entry[10]);
  187. if (hanliang != rgde[j]["gr"]) {
  188. //console.log(`[${i}, 10]hanliang bu yizhi`);
  189. toHighlight.push({row: i, col: 10, renderer: "customStylesRenderer"});
  190. }
  191. }
  192. }
  193. }
  194. if (Number(rcjlb) == Number(3) && jxde ) {
  195. for (let j = 0; j < jxde.length; j++) {
  196. if (jxde[j]["jxbh"] == bianhao) {
  197. if(jxde[j]["jxmc"] == entry[2])hit = true;
  198. let danjia = Number(entry[5]);
  199. if (danjia != jxde[j]["tbdj"]) {
  200. //console.log(`[${i},5]danjia bu yizhi`);
  201. toHighlight.push({row: i, col: 5, renderer: "customStylesRenderer"});
  202. }
  203. let hanliang = Number(entry[10]);
  204. if (hanliang != jxde[j]["sl"]) {
  205. //console.log(`[${i}, 10]hanliang bu yizhi`);
  206. toHighlight.push({row: i, col: 10, renderer: "customStylesRenderer"});
  207. }
  208. }
  209. }
  210. }
  211. if (Number(rcjlb) == Number(2) && clde ) {
  212. for (let j = 0; j < clde.length; j++) {
  213. if (clde[j]["CLBH"] == bianhao) {
  214. if(clde[j]["CLMC"] == entry[2])hit = true;
  215. let danjia = Number(entry[5]);
  216. if (danjia != clde[j]["YSJG"]) {
  217. //console.log(`[${i},5]danjia bu yizhi`);
  218. toHighlight.push({row: i, col: 5, renderer: "customStylesRenderer"});
  219. }
  220. let hanliang = Number(entry[10]);
  221. if (hanliang != clde[j]["SL"]) {
  222. //console.log(`[${i}, 10]hanliang bu yizhi`);
  223. toHighlight.push({row: i, col: 10, renderer: "customStylesRenderer"});
  224. }
  225. }
  226. }
  227. }
  228. if (!hit) {
  229. toHighlight.push({row: i, col: 1, renderer: "customStylesRenderer"});
  230. }
  231. }
  232. //console.log(toHighlight);
  233. highlight.current = toHighlight;
  234. }
  235. let bzrcjhl = [["人材机编码", "名称", "单位", "单价", "合价", "含量"]]
  236. if (rgde)
  237. for (let i = 0; i < rgde.length; i++) {
  238. bzrcjhl.push([rgde[i]["CLBH"], rgde[i]["CLMC"], rgde[i]["JLDW"], rgde[i]["YSJG"], rgde[i]["gf"], rgde[i]["gr"]]);
  239. }
  240. if (clde)
  241. for (let i = 0; i < clde.length; i++) {
  242. bzrcjhl.push([clde[i]["CLBH"], clde[i]["CLMC"], clde[i]["JLDW"], clde[i]["YSJG"], clde[i]["HJ"], clde[i]["SL"]]);
  243. }
  244. if (jxde)
  245. for (let i = 0; i < jxde.length; i++) {
  246. bzrcjhl.push([jxde[i]["jxbh"], jxde[i]["jxmc"], jxde[i]["DW"], jxde[i]["tbdj"], jxde[i]["hj"], jxde[i]["sl"]]);
  247. }
  248. setRcjhl2(bzrcjhl);
  249. }, [rgde, jxde, clde]
  250. );
  251. const handleChange = (event, newValue) => {
  252. setValue(newValue);
  253. };
  254. const afterUpdateData = (x, initial, source)=>{
  255. console.log(source);
  256. let plugin = hotRef.current?.hotInstance.getPlugin("NestedRows");
  257. let ui = plugin.collapsingUI;
  258. //ui.collapseRow(1);
  259. hotRef.current?.hotInstance?.scrollViewportTo({ row: selectedRow.current });
  260. };
  261. const handleSelection = (row, column) => {
  262. selectedRow.current = row;
  263. let selected = hotRef.current?.hotInstance?.getData()[row];
  264. if (!selected) return;
  265. if (selected[1] != null) {
  266. Service.generateQingdanrcj(name, bh,bt,selected[2]).then(x=>{
  267. //setRcjhl(x);
  268. hotRcjRef.current?.hotInstance?.loadData(x);
  269. isQdrcj.current = true;
  270. highlight.current = [];
  271. });
  272. Service.generateQingdanTuijian(name, bh,bt,selected[2]).then(x=>{
  273. //setTuijian(x);
  274. hotTuijianRef.current?.hotInstance?.loadData(x);
  275. //isQdrcj.current = true;
  276. //setHighlight([]);
  277. });
  278. }else{
  279. let qdbm = null;
  280. for (let i = row - 1; i > -1; i= i - 1) {
  281. let above = hotRef.current?.hotInstance?.getData()[i];
  282. if (above[1] != null) {
  283. qdbm = above[2];
  284. break;
  285. }
  286. }
  287. //console.log(name, bh,bt,qdbm, selected[1]);
  288. clickCallback(qdbm, selected[2]);
  289. Service.generateDingercj(name, bh,bt,qdbm, selected[2], selected[6]).then(x=>{
  290. console.log(x);
  291. //setRcjhl(x);
  292. hotRcjRef.current?.hotInstance?.loadData(x);
  293. isQdrcj.current = false;
  294. let toHighlight = [];
  295. for(let i = 1; i < x.length; i++) {
  296. let entry = x[i];
  297. let bianhao = entry[1];
  298. let rcjlb = entry[8];
  299. let hit = false;
  300. if (Number(rcjlb) == 1 && rgdeRef.current ) {
  301. for (let j = 0;j < rgdeRef.current.length; j++) {
  302. if (rgdeRef.current[j]["CLBH"] == bianhao) {
  303. if(rgdeRef.current[j]["CLMC"] == entry[2])hit = true;
  304. let danjia = Number(entry[5]);
  305. if (danjia != rgdeRef.current[j]["YSJG"]) {
  306. //console.log(`[${i},5]danjia bu yizhi`);
  307. toHighlight.push({row: i, col: 5, renderer: "customStylesRenderer"});
  308. }
  309. let hanliang = Number(entry[10]);
  310. if (hanliang != rgdeRef.current[j]["gr"]) {
  311. //console.log(`[${i}, 10]hanliang bu yizhi`);
  312. toHighlight.push({row: i, col: 10, renderer: "customStylesRenderer"});
  313. }
  314. }
  315. }
  316. }
  317. if (Number(rcjlb) == 3 && jxdeRef.current ) {
  318. for (let j = 0; j < jxdeRef.current.length; j++) {
  319. if (jxdeRef.current[j]["jxbh"] == bianhao) {
  320. if(jxdeRef.current[j]["jxmc"] == entry[2])hit = true;
  321. let danjia = Number(entry[5]);
  322. if (danjia != jxdeRef.current[j]["tbdj"]) {
  323. //console.log(`[${i},5]danjia bu yizhi`);
  324. toHighlight.push({row: i, col: 5, renderer: "customStylesRenderer"});
  325. }
  326. let hanliang = Number(entry[10]);
  327. if (hanliang != jxdeRef.current[j]["sl"]) {
  328. //console.log(`[${i}, 10]hanliang bu yizhi`);
  329. toHighlight.push({row: i, col: 10, renderer: "customStylesRenderer"});
  330. }
  331. }
  332. }
  333. }
  334. if (Number(rcjlb) == 2 && cldeRef.current ) {
  335. for (let j = 0; j < cldeRef.current.length; j++) {
  336. if (cldeRef.current[j]["CLBH"] == bianhao) {
  337. if(cldeRef.current[j]["CLMC"] == entry[2])hit = true;
  338. let danjia = Number(entry[5]);
  339. if (danjia != cldeRef.current[j]["YSJG"]) {
  340. //console.log(`[${i},5]danjia bu yizhi`);
  341. toHighlight.push({row: i, col: 5, renderer: "customStylesRenderer"});
  342. }
  343. let hanliang = Number(entry[10]);
  344. if (hanliang != cldeRef.current[j]["SL"]) {
  345. //console.log(`[${i}, 10]hanliang bu yizhi`);
  346. toHighlight.push({row: i, col: 10, renderer: "customStylesRenderer"});
  347. }
  348. }
  349. }
  350. }
  351. if (!hit) {
  352. toHighlight.push({row: i, col: 1, renderer: "customStylesRenderer"});
  353. }
  354. }
  355. highlight.current = toHighlight;
  356. });
  357. }
  358. hotRef.current?.hotInstance?.render();
  359. };
  360. return (
  361. <Stack spacing={2}>
  362. <Box>
  363. <Stack direction='row' spacing={2}>
  364. <Button variant="outlined" onClick={() => {
  365. const [success, data] = danxiangdinge(selectedRow.current);
  366. if(success) {
  367. setDetail(data);
  368. }
  369. }}
  370. >单项定额</Button>
  371. <Button variant="outlined" onClick={() => {
  372. setDetail(undo());
  373. }}
  374. >撤销</Button>
  375. <Button variant="outlined" onClick={() => {
  376. setDetail(redo());
  377. }}
  378. >重做</Button>
  379. </Stack>
  380. <HotTable
  381. nestedRows={true}
  382. data={detail
  383. }
  384. afterUpdateData={afterUpdateData}
  385. beforeUpdateData={(a,b,c)=>{
  386. let plugin = hotRef.current?.hotInstance.getPlugin("NestedRows");
  387. let ui = plugin.collapsingUI;
  388. console.log(ui);
  389. }}
  390. //afterSelection={handleSelection}
  391. afterSelection={handleSelection}
  392. ref = {hotRef}
  393. cells={(row, col, prop) => {
  394. return {
  395. // row options, which apply to each cell of the second row
  396. // and to each cell of the fifth row
  397. renderer: "highlightRowRenderer"
  398. };
  399. }
  400. }
  401. colWidths={(index) => {
  402. if (index == 4 || index == 5) return 300;
  403. else return 100;
  404. }}
  405. readOnly={true}
  406. /*afterUndo={(action) => {
  407. console.log(action);
  408. console.log(hotRef.current?.hotInstance?.getData());
  409. }}*/
  410. //trimRows={true}
  411. contextMenu={false}
  412. bindRowsWithHeaders={true}
  413. fixedRowsTop={0}
  414. fixedColumnsStart={2}
  415. manualColumnResize={true}
  416. rowHeaders={true}
  417. columns = {[
  418. { data: '操作' },
  419. { data: '序号' },
  420. { data: '清单编码' },
  421. { data: '名称' },
  422. { data: '项目特征' },
  423. { data: '计算规则' },
  424. { data: '单位' },
  425. { data: '数量' },
  426. { data: '综合单价' },
  427. { data: '合价' },
  428. { data: '人工费' },
  429. { data: '主材费' },
  430. { data: '设备费' },
  431. { data: '辅材费' },
  432. { data: '材料费' },
  433. { data: '机械费' },
  434. { data: '管理费' },
  435. { data: '利润' },
  436. { data: '暂估价' },
  437. { data: '综合人工工日' },
  438. { data: '备注' },
  439. ]}
  440. colHeaders={["操作", "序号", "清单编码", "名称", "项目特征", "计算规则", "单位", "数量", "综合单价", "合价", "人工费", "主材费",
  441. "设备费", "辅材费", "材料费", "机械费", "管理费", "利润" , "暂估价", "综合人工工日", "备注" ]}
  442. height="400"
  443. selectionMode="single"
  444. autoWrapRow={false}
  445. autoWrapCol={false}
  446. licenseKey="non-commercial-and-evaluation" // for non-commercial use only
  447. />
  448. </Box>
  449. <Box >
  450. <TabContext value={value}>
  451. <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
  452. <TabList onChange={handleChange} aria-label="lab API tabs example">
  453. <Tab label="人材机含量" value="1" />
  454. <Tab label="标准定额人材机含量" value="2" />
  455. <Tab label="定额附注" value="3" />
  456. <Tab label="组价推荐" value="4" />
  457. </TabList>
  458. </Box>
  459. <TabPanel value="1">
  460. <HotTable
  461. nestedRows={false}
  462. data={rcjhl
  463. }
  464. //cell={highlight}
  465. manualColumnResize={true}
  466. rowHeaders={true}
  467. colHeaders={true}
  468. height="300"
  469. formulas={{
  470. engine: hyperformulaInstance,
  471. sheetName: 'Rcj',
  472. }}
  473. cells={(row, col) => {
  474. if (row === 0) {
  475. return { readOnly: true, renderer: "customStylesRenderer" };
  476. }
  477. if (col <= 1 || col === 11) {
  478. return { readOnly: true, renderer: "customStylesRenderer" };
  479. }
  480. return {renderer: "customStylesRenderer"};
  481. }}
  482. fixedRowsTop={1}
  483. selectionMode="single"
  484. autoWrapRow={false}
  485. autoWrapCol={false}
  486. ref = {hotRcjRef}
  487. afterChange={(changes, source) => {
  488. //console.log(hotRcjRef.current?.hotInstance?.getData());
  489. updateDercj(selectedRow.current, hotRcjRef.current?.hotInstance?.getData());
  490. }}
  491. licenseKey="non-commercial-and-evaluation" // for non-commercial use only
  492. />
  493. </TabPanel>
  494. <TabPanel value="2">
  495. <HotTable
  496. nestedRows={false}
  497. data={rcjhl2
  498. }
  499. manualColumnResize={true}
  500. rowHeaders={true}
  501. colHeaders={true}
  502. height="300"
  503. readOnly={true}
  504. fixedRowsTop={1}
  505. selectionMode="single"
  506. autoWrapRow={false}
  507. autoWrapCol={false}
  508. licenseKey="non-commercial-and-evaluation" // for non-commercial use only
  509. />
  510. </TabPanel>
  511. <TabPanel value="3">
  512. <HotTable
  513. nestedRows={false}
  514. data={fuzhu
  515. }
  516. manualColumnResize={true}
  517. rowHeaders={true}
  518. colHeaders={true}
  519. height="300"
  520. readOnly={true}
  521. fixedRowsTop={1}
  522. selectionMode="single"
  523. autoWrapRow={false}
  524. autoWrapCol={false}
  525. licenseKey="non-commercial-and-evaluation" // for non-commercial use only
  526. />
  527. </TabPanel>
  528. <TabPanel value="4">
  529. <HotTable
  530. nestedRows={false}
  531. data={tuijian
  532. }
  533. ref = {hotTuijianRef}
  534. manualColumnResize={true}
  535. rowHeaders={true}
  536. colHeaders={true}
  537. height="300"
  538. readOnly={true}
  539. fixedRowsTop={1}
  540. selectionMode="single"
  541. autoWrapRow={false}
  542. autoWrapCol={false}
  543. licenseKey="non-commercial-and-evaluation" // for non-commercial use only
  544. />
  545. </TabPanel>
  546. </TabContext>
  547. </Box>
  548. </Stack>
  549. );
  550. }