LuckySheet.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. import React from 'react';
  2. import { Button, message } from 'antd';
  3. import exportExcel, { getExcelBolob } from '@/utils/exportExcl';
  4. import LuckyExcel from 'luckyexcel';
  5. import { getToken, GetTokenFromUrl } from '@/utils/utils';
  6. import GoalSeek from '@/utils/GoalSeek';
  7. const hintText = '禁止编辑!请先点击编辑按钮。';
  8. const DIFF_COLOR = '#ff0000';
  9. const ADD_COLOR = '#00ff00';
  10. class LuckySheet extends React.Component {
  11. constructor(props) {
  12. super(props);
  13. this.sheetRef = React.createRef();
  14. this.luckysheet = null;
  15. this.updateTimer = null;
  16. this.currentSheetIndex = null;
  17. this.updateCell = {
  18. add: [],
  19. diff: [],
  20. };
  21. this.renderTimer = null;
  22. this.chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
  23. }
  24. componentWillUnmount() {
  25. this.luckysheet?.destroy();
  26. }
  27. componentDidUpdate(prveProps) {
  28. const prevVersion = prveProps.version || {};
  29. const curVersion = this.props.version || {};
  30. if (prevVersion?.id != curVersion?.id || prevVersion?.flow_id != curVersion.flow_id) {
  31. console.log(prevVersion, curVersion);
  32. this.renderSheet();
  33. }
  34. }
  35. getUUID(len = 8, radix = 16) {
  36. var chars = this.chars;
  37. var uuid = [],
  38. i;
  39. radix = radix || chars.length;
  40. if (len) {
  41. // Compact form
  42. for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
  43. } else {
  44. // rfc4122, version 4 form
  45. var r;
  46. // rfc4122 requires these characters
  47. uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
  48. uuid[14] = '4';
  49. // Fill in random data. At i==19 set the high bits of clock sequence as
  50. // per rfc4122, sec. 4.1.5
  51. for (i = 0; i < 36; i++) {
  52. if (!uuid[i]) {
  53. r = 0 | (Math.random() * 16);
  54. uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
  55. }
  56. }
  57. }
  58. return uuid.join('');
  59. }
  60. renderSheet(currentData) {
  61. const { onClickCell, version, getUser, onUpdate, templateId, onDelSheet } = this.props;
  62. const data = currentData || this.props.data;
  63. const _this = this;
  64. if (!this.luckysheet) {
  65. clearTimeout(this.renderTimer);
  66. this.renderTimer = setTimeout(() => {
  67. this.renderSheet(currentData);
  68. }, 300);
  69. return;
  70. }
  71. let token = GetTokenFromUrl() || getToken();
  72. let option = {
  73. lang: 'zh',
  74. showinfobar: false,
  75. showstatisticBar: false,
  76. // forceCalculation: true,
  77. hook: {
  78. cellMousedown: (cell, position, sheet) => {
  79. console.log(cell, position, sheet);
  80. onClickCell && onClickCell(cell, position, sheet);
  81. },
  82. cellPasteBefore: cell => {
  83. console.log(cell);
  84. if (!cell) return;
  85. if (cell.cid) delete cell.cid;
  86. if (cell.bg == DIFF_COLOR || cell.bg == ADD_COLOR) {
  87. delete cell.bg;
  88. }
  89. },
  90. updated(operate) {
  91. if (operate.type == 'datachange') {
  92. if (_this.currentSheetIndex != operate.sheetIndex) {
  93. _this.currentSheetIndex = operate.sheetIndex;
  94. return;
  95. }
  96. // 延迟1秒
  97. clearTimeout(_this.updateTimer);
  98. _this.updateTimer = setTimeout(() => {
  99. onUpdate.bind(_this);
  100. onUpdate();
  101. }, 1000);
  102. }
  103. },
  104. // 修改批注后保存sheet
  105. commentUpdateAfter() {
  106. clearTimeout(_this.updateTimer);
  107. _this.updateTimer = setTimeout(() => {
  108. onUpdate.bind(_this);
  109. onUpdate(true);
  110. }, 1000);
  111. },
  112. sheetActivate: sheet => {
  113. console.log(sheet);
  114. setTimeout(() => {
  115. this.luckysheet.setCellFormat(0, 0, 'bg', '#fff');
  116. }, 100);
  117. },
  118. sheetDeleteAfter: sheet => {
  119. onDelSheet && onDelSheet(sheet?.sheet.id);
  120. },
  121. sheetActivate: sheet => {
  122. console.log(sheet);
  123. setTimeout(() => {
  124. this.luckysheet.setCellFormat(0, 0, 'bg', '#fff');
  125. }, 100);
  126. },
  127. },
  128. };
  129. if (version) {
  130. const wsUrl =
  131. process.env.NODE_ENV == 'development'
  132. ? 'ws://47.96.12.136:8896/'
  133. : `ws://${location.host}/`;
  134. option = {
  135. ...option,
  136. allowUpdate: true,
  137. gridKey: version.id,
  138. templateId: templateId,
  139. // flowId: version.flow_id,
  140. loadUrl: `/api/v1/purchase/record/sheet?gridKey=${version.id}&JWT-TOKEN=${token}`,
  141. updateUrl: wsUrl + `api/v1/ws?id=${version.id}&sid=${templateId}&JWT-TOKEN=${token}`,
  142. // updateUrl: `ws://47.96.12.136:8896/api/v1/ws?id=${version.id}&sid=${templateId}&JWT-TOKEN=${token}`,
  143. // updateUrl: `ws://120.55.44.4:8896/api/v1/ws?id=${version.id}&sid=${templateId}&JWT-TOKEN=${token}`,
  144. authorityUrl: `/api/v1/purchase/bom/user/excel/col?depId=${localStorage.depId ||
  145. 0}&JWT-TOKEN=${token}`,
  146. getUser,
  147. // workbookCreateBefore(luckysheet) {
  148. // console.log('===============================', luckysheet);
  149. // let oldConfig = JSON.parse(JSON.stringify(luckysheet.getConfig()));
  150. // setTimeout(() => {
  151. // luckysheet.setConfig({
  152. // ...oldConfig,
  153. // authority: { sheet: true, hintText },
  154. // });
  155. // }, 300);
  156. // },
  157. };
  158. console.log(version);
  159. const unableEdit = option => {
  160. option.showtoolbar = false;
  161. option.enableAddRow = false;
  162. option.sheetFormulaBar = false;
  163. option.enableAddBackTop = false;
  164. option.showsheetbarConfig = {
  165. add: false,
  166. sheet: false,
  167. };
  168. option.cellRightClickConfig = {
  169. copy: false, // 复制
  170. copyAs: false, // 复制为
  171. paste: false, // 粘贴
  172. insertRow: false, // 插入行
  173. insertColumn: false, // 插入列
  174. deleteRow: false, // 删除选中行
  175. deleteColumn: false, // 删除选中列
  176. deleteCell: false, // 删除单元格
  177. hideRow: false, // 隐藏选中行和显示选中行
  178. hideColumn: false, // 隐藏选中列和显示选中列
  179. rowHeight: false, // 行高
  180. columnWidth: false, // 列宽
  181. clear: false, // 清除内容
  182. matrix: false, // 矩阵操作选区
  183. sort: false, // 排序选区
  184. filter: false, // 筛选选区
  185. chart: false, // 图表生成
  186. image: false, // 插入图片
  187. link: false, // 插入链接
  188. data: false, // 数据验证
  189. cellFormat: false, // 设置单元格格式
  190. };
  191. };
  192. if (version.flow_id) {
  193. option.authority = {
  194. sheet: true,
  195. hintText: '当前处于审批节点,禁止编辑!',
  196. };
  197. unableEdit(option);
  198. } else if (version.last_version) {
  199. option.authority = {
  200. sheet: true,
  201. hintText: '该清单已设置为最终版本,禁止编辑!',
  202. };
  203. unableEdit(option);
  204. } else if ((version.audit_status != 0 && version.audit_status != 5) || version.status == 1) {
  205. option.authority = {
  206. sheet: true,
  207. hintText: '当前清单不可编辑!',
  208. };
  209. unableEdit(option);
  210. }
  211. } else if (data && data.length > 0) {
  212. option.data = JSON.parse(JSON.stringify(data));
  213. option.data.forEach(item => {
  214. if (item.celldata) {
  215. item.celldata.forEach(cell => {
  216. // 生成uuid
  217. if (!cell.v.cid) cell.v.cid = this.getUUID();
  218. });
  219. }
  220. // 默认禁止编辑
  221. // item.config.authority = { sheet: true, hintText };
  222. });
  223. } else {
  224. // 默认sheet页数据
  225. data.data = [
  226. {
  227. name: 'sheet1',
  228. // config: {
  229. // authority: { sheet: true, hintText },
  230. // },
  231. },
  232. ];
  233. }
  234. this.luckysheet.destroy();
  235. this.luckysheet.create(option);
  236. // 比对模式会导致单元格出现[Object object]的情况 任意编辑后才会正常显示
  237. // 所以默认设置第一个单元格的背景色
  238. setTimeout(() => {
  239. this.luckysheet.setCellFormat(0, 0, 'bg', '#fff');
  240. }, 500);
  241. }
  242. // componentDidUpdate(prevProps) {
  243. // const { data } = this.props;
  244. // if (prevProps.data != data) {
  245. // this.renderSheet(data);
  246. // }
  247. // }
  248. handleLoad() {
  249. const { version } = this.props;
  250. let contentWindow = this.sheetRef.current.contentWindow;
  251. this.luckysheet = contentWindow.luckysheet;
  252. // this.luckysheet = this.luckysheet;
  253. // version存在 则需调用render
  254. if (version) {
  255. this.renderSheet();
  256. }
  257. // onLoad && onLoad();
  258. }
  259. selectCell(row, col, order) {
  260. this.luckysheet.setRangeShow({ row: [row, row], column: [col, col] }, { order });
  261. }
  262. toggleSheet(order) {
  263. this.luckysheet.setSheetActive(order);
  264. }
  265. getSheetJson() {
  266. let data = JSON.parse(JSON.stringify(this.luckysheet.toJson()));
  267. data.data.forEach(sheet => {
  268. let allCell = {},
  269. unknowCid = [];
  270. // 将cell以cid为界分别存储
  271. (sheet.celldata || []).forEach(cell => {
  272. if (!cell.v.cid) {
  273. unknowCid.push(cell);
  274. } else if (!allCell[cell.v.cid]) {
  275. allCell[cell.v.cid] = cell;
  276. } else {
  277. // 当存在相同cid时
  278. // 做异常处理
  279. delete cell.v.cid;
  280. unknowCid.push(cell);
  281. }
  282. if (cell.v.tb) cell.v.tb = Number(cell.v.tb);
  283. // 清除比对样式
  284. if (cell.v.bg == DIFF_COLOR || cell.v.bg == ADD_COLOR) {
  285. delete cell.v.bg;
  286. }
  287. });
  288. unknowCid.forEach(cell => {
  289. // 根据坐标生成唯一key,重复则增加后缀直至不重复
  290. let key = `${cell.r}-${cell.c}`;
  291. while (allCell[key]) {
  292. key += '|c';
  293. }
  294. cell.v.cid = key;
  295. allCell[key] = cell;
  296. });
  297. sheet.celldata = Object.values(allCell);
  298. });
  299. return data;
  300. }
  301. // 切换编辑状态
  302. toggleEdit(edit) {
  303. let luckysheet = this.luckysheet;
  304. if (edit) {
  305. let config = luckysheet.getConfig();
  306. luckysheet.setConfig({
  307. ...config,
  308. authority: { sheet: !edit, hintText },
  309. });
  310. } else {
  311. luckysheet.exitEditMode();
  312. }
  313. }
  314. // 切换比对状态
  315. toggleCompare(isCompare, compareData, callback) {
  316. let luckysheet = this.luckysheet;
  317. let diff = [];
  318. let add = [];
  319. const { onCompareSuccess } = this.props;
  320. // 判断dom是否加载完成
  321. if (!luckysheet) {
  322. setTimeout(() => {
  323. this.toggleCompare(isCompare, compareData, callback);
  324. }, 300);
  325. return;
  326. }
  327. if (isCompare) {
  328. // let currentData = this.luckysheet.toJson();
  329. let currentData = JSON.parse(JSON.stringify(this.props.data));
  330. currentData.forEach((sheet, index) => {
  331. let celldata1 = sheet.celldata;
  332. let celldata2 = compareData[index]?.celldata || [];
  333. celldata1.forEach(item => {
  334. // 不判断空字符串
  335. if (this.isEmpty(item)) return;
  336. var d2Item = celldata2.find(item2 => item2.v.cid == item.v.cid);
  337. if (d2Item && !this.isEmpty(d2Item)) {
  338. // v.ct.s相同,不做处理
  339. if (item.v.ct?.s && JSON.stringify(item.v.ct?.s) == JSON.stringify(d2Item.v.ct?.s))
  340. return;
  341. // v.v相同,不做处理
  342. if (d2Item.v.v == item.v.v) return;
  343. // 内容不同,标记diff颜色
  344. diff.push({
  345. ...item,
  346. sheetOrder: index,
  347. });
  348. item.v.bg = DIFF_COLOR;
  349. // luckysheet.setCellFormat(item.r, item.c, 'bg', DIFF_COLOR);
  350. } else {
  351. // 找不到同cid的单元格,标记add颜色
  352. add.push({
  353. ...item,
  354. sheetOrder: index,
  355. });
  356. item.v.bg = ADD_COLOR;
  357. // luckysheet.setCellFormat(item.r, item.c, 'bg', ADD_COLOR);
  358. }
  359. });
  360. });
  361. console.log(currentData);
  362. this.renderSheet(currentData);
  363. // luckysheet.refresh()
  364. } else {
  365. this.renderSheet(this.props.data);
  366. }
  367. this.updateCell = {
  368. diff,
  369. add,
  370. };
  371. callback && callback(this.updateCell);
  372. }
  373. isEmpty(item) {
  374. return (item?.v?.v ?? '') === '' && !item?.v?.ct?.s;
  375. }
  376. mergeExcl(updateCell = {}) {
  377. const { diff = [], add = [] } = updateCell;
  378. let currentData = this.luckysheet.toJson().data;
  379. let luckysheet = this.luckysheet;
  380. console.log(updateCell);
  381. diff.forEach(item => {
  382. let sheet = currentData[item.sheetOrder];
  383. let d1Item = sheet.celldata.find(item2 => item2.v.cid == item.v.cid);
  384. // 将差异项覆盖至当前文档
  385. d1Item.v = {
  386. ...item.v,
  387. bg: undefined,
  388. };
  389. });
  390. add.forEach(item => {
  391. // 将新增项添加至当前文档
  392. let sheet = currentData[item.sheetOrder];
  393. let d1Item = sheet.celldata.find(item2 => item2.r == item.r && item2.c == item.c);
  394. if (d1Item) {
  395. d1Item.v = {
  396. ...item.v,
  397. bg: undefined,
  398. };
  399. } else {
  400. sheet.celldata.push({
  401. ...item,
  402. sheetOrder: undefined,
  403. });
  404. }
  405. });
  406. currentData.forEach(sheet => {
  407. delete sheet.data;
  408. });
  409. this.renderSheet(currentData);
  410. // currentData.data.forEach((sheet, index) => {
  411. // if (!mergeData[index]) return;
  412. // let celldata1 = sheet.celldata;
  413. // let celldata2 = mergeData[index].celldata;
  414. // celldata2.forEach(item => {
  415. // let bg = item.v?.bg;
  416. // let d1Item;
  417. // if (bg == DIFF_COLOR) {
  418. // delete item.v.bg;
  419. // d1Item = celldata1.find(item2 => item2.v.cid == item.v.cid);
  420. // // 将差异项覆盖至当前文档
  421. // d1Item.v = item.v;
  422. // // luckysheet.setCellValue(d1Item.r, d1Item.c, item.v);
  423. // } else if (bg == ADD_COLOR) {
  424. // delete item.v.bg;
  425. // // 将新增项添加至当前文档
  426. // // luckysheet.setCellValue(item.r, item.c, item.v);
  427. // d1Item = celldata1.find(item2 => item2.r == item.r && item2.c == item.c);
  428. // if (d1Item) {
  429. // d1Item.v = item.v;
  430. // } else {
  431. // celldata1.push(item);
  432. // }
  433. // }
  434. // });
  435. // });
  436. // this.renderSheet(currentData);
  437. }
  438. /**
  439. * 导入excl
  440. * @param {*} files input:file的evt.target.files
  441. * @returns
  442. */
  443. uploadExcel(files, callback) {
  444. if (files == null || files.length == 0) {
  445. return;
  446. }
  447. let name = files[0].name;
  448. let suffixArr = name.split('.'),
  449. suffix = suffixArr[suffixArr.length - 1];
  450. if (suffix != 'xlsx') {
  451. alert('Currently only supports the import of xlsx files');
  452. message.error('只支持xlsx格式的文件!');
  453. return;
  454. }
  455. LuckyExcel.transformExcelToLucky(files[0], (exportJson, luckysheetfile) => {
  456. if (exportJson.sheets == null || exportJson.sheets.length == 0) {
  457. message.error('读取xlsx文件失败!');
  458. return;
  459. }
  460. // this.luckysheet.destroy();
  461. // 同步当前文档内容
  462. let data = this.props.data;
  463. exportJson.sheets.forEach((sheet, index) => {
  464. if (!data || !data[index]) return;
  465. sheet.celldata.forEach(cell => {
  466. if (this.isEmpty(cell)) return;
  467. // return (item.v.v ?? '') === '' && !item.v.ct?.s;
  468. let dCell = (data[index].celldata || []).find(dCell => {
  469. return dCell.r == cell.r && dCell.c == cell.c;
  470. });
  471. if (this.isEmpty(dCell)) return;
  472. // 判断v.ct是否相同
  473. // if (cell?.v?.ct?.s && dCell.v.ct?.s && cell.v.ct?.s.join('') != dCell.v.ct?.s.join('')) return;
  474. if (cell?.v?.ct?.s && dCell.v.ct?.s) {
  475. if (cell.v.ct?.s.join('') != dCell.v.ct?.s.join('')) return;
  476. let cellS = cell.v.ct.s;
  477. let dCellS = dCell.v.ct.s;
  478. let isEqul = cellS.every((cur, idx) => {
  479. return JSON.stringify(cur) === JSON.stringify(dCellS[idx]);
  480. });
  481. if (!isEqul) return;
  482. }
  483. // 判断v.v是否相同
  484. if (cell?.v?.v && dCell.v.v != cell.v.v) return;
  485. // 内容相同则复制cid
  486. cell.cid = dCell.cid;
  487. });
  488. });
  489. this.renderSheet(exportJson.sheets);
  490. callback && callback();
  491. });
  492. }
  493. // 根据url导入excl
  494. // selectExcel(item) {
  495. // const {value,name} = item
  496. // if (value == '') {
  497. // return;
  498. // }
  499. // LuckyExcel.transformExcelToLuckyByUrl(value, name, (exportJson, luckysheetfile) => {
  500. // if (exportJson.sheets == null || exportJson.sheets.length == 0) {
  501. // alert(
  502. // 'Failed to read the content of the excel file, currently does not support xls files!'
  503. // );
  504. // return;
  505. // }
  506. // this.luckysheet.destroy();
  507. // this.luckysheet.create({
  508. // container: 'luckysheet', //luckysheet is the container id
  509. // showinfobar: false,
  510. // data: exportJson.sheets,
  511. // title: exportJson.info.name,
  512. // userInfo: exportJson.info.name.creator,
  513. // });
  514. // });
  515. // }
  516. getExcelData(checkValue = null) {
  517. let resultList = [];
  518. console.log(this.luckysheet.getAllSheets());
  519. let currentData = this.luckysheet.getAllSheets();
  520. currentData.forEach(sheet => {
  521. let data = sheet.data;
  522. let celldata = sheet.celldata;
  523. let colList = [];
  524. data[0]?.forEach((rowOneItem, colIdx) => {
  525. if (rowOneItem) {
  526. if (!checkValue || checkValue.indexOf(rowOneItem.cid) !== -1) {
  527. colList.indexOf(colIdx) == -1 ? colList.push(colIdx) : true;
  528. }
  529. }
  530. });
  531. const newData = [];
  532. data.forEach(item => {
  533. if (item !== null) {
  534. let arr = item.filter((cur, idx) => {
  535. return item && colList.includes(idx);
  536. });
  537. newData.push(arr);
  538. }
  539. });
  540. sheet.data = newData;
  541. //消除空列后都列下标
  542. let newColIdxList = colList.map((cur, idx) => {
  543. return idx;
  544. });
  545. //处理celldata
  546. const newCellData = [];
  547. celldata.forEach(item => {
  548. let idx = colList.indexOf(item.c);
  549. if (idx !== -1) {
  550. item.c = newColIdxList[idx];
  551. newCellData.push(item);
  552. }
  553. });
  554. sheet.celldata = newCellData;
  555. });
  556. return currentData;
  557. }
  558. getExcelBolb() {
  559. let currentData = this.getExcelData();
  560. return getExcelBolob(currentData);
  561. }
  562. downloadExcel(checkValue) {
  563. let currentData = this.getExcelData(checkValue);
  564. exportExcel(currentData, '下载');
  565. }
  566. // 获取批注
  567. getComment() {
  568. let sheets = this.luckysheet.toJson().data;
  569. let comment = [];
  570. sheets.forEach(sheet => {
  571. sheet.celldata.forEach(cell => {
  572. // 判断是否含有批注
  573. if (cell?.v?.ps?.value) {
  574. comment.push({
  575. sheet: sheet.name,
  576. r: cell.r,
  577. c: cell.c,
  578. value: cell.v.ps.value || '',
  579. });
  580. }
  581. });
  582. });
  583. return comment;
  584. }
  585. async goalSeek(type, goal) {
  586. let luckysheet = this.luckysheet;
  587. const fn = function(x) {
  588. return new Promise(resolve => {
  589. luckysheet.setCellValue(9, 2, x.toFixed(4), {
  590. order: 0,
  591. success: () => {
  592. luckysheet.refreshFormula(() => {
  593. let row;
  594. if (type == 1) {
  595. row = 1;
  596. } else if (type == 2) {
  597. row = 4;
  598. } else {
  599. row = 5;
  600. }
  601. let data = luckysheet.getCellValue(row, 2, {
  602. order: 0,
  603. });
  604. console.log(data);
  605. resolve(data);
  606. });
  607. },
  608. });
  609. });
  610. };
  611. try {
  612. let defaultValue = luckysheet.getCellValue(9, 2, {
  613. order: 0,
  614. });
  615. const result = await GoalSeek({
  616. goal,
  617. fn,
  618. fnParams: [defaultValue],
  619. maxIterations: 1000,
  620. maxStep: 0.03,
  621. percentTolerance: 1,
  622. independentVariableIdx: 0,
  623. });
  624. console.log(result);
  625. } catch (error) {
  626. console.log(error);
  627. }
  628. }
  629. render() {
  630. return (
  631. <iframe
  632. onLoad={e => {
  633. this.handleLoad(e);
  634. }}
  635. ref={this.sheetRef}
  636. src="/luckysheet.html"
  637. ></iframe>
  638. );
  639. }
  640. }
  641. export default LuckySheet;