LuckySheet.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. import React from 'react';
  2. import { message } from 'antd';
  3. import exportExcel from '@/utils/exportExcl';
  4. import LuckyExcel from 'luckyexcel';
  5. import { getToken } from '@/utils/utils';
  6. const hintText = '禁止编辑!请先点击编辑按钮。';
  7. const DIFF_COLOR = '#ff0000';
  8. const ADD_COLOR = '#00ff00';
  9. class LuckySheet extends React.Component {
  10. constructor(props) {
  11. super(props);
  12. this.sheetRef = React.createRef();
  13. this.luckysheet = null;
  14. this.updateCell = {
  15. add: [],
  16. diff: [],
  17. };
  18. this.renderTimer = null;
  19. this.chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
  20. }
  21. getUUID(len = 8, radix = 16) {
  22. var chars = this.chars;
  23. var uuid = [],
  24. i;
  25. radix = radix || chars.length;
  26. if (len) {
  27. // Compact form
  28. for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
  29. } else {
  30. // rfc4122, version 4 form
  31. var r;
  32. // rfc4122 requires these characters
  33. uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
  34. uuid[14] = '4';
  35. // Fill in random data. At i==19 set the high bits of clock sequence as
  36. // per rfc4122, sec. 4.1.5
  37. for (i = 0; i < 36; i++) {
  38. if (!uuid[i]) {
  39. r = 0 | (Math.random() * 16);
  40. uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
  41. }
  42. }
  43. }
  44. return uuid.join('');
  45. }
  46. renderSheet(sheets) {
  47. // if (!sheets) return;
  48. const { onClickCell, version, getUser, data } = this.props;
  49. if (!this.luckysheet) {
  50. clearTimeout(this.renderTimer);
  51. this.renderTimer = setTimeout(() => {
  52. this.renderSheet(sheets);
  53. }, 300);
  54. return;
  55. }
  56. let token = getToken();
  57. let option = {
  58. lang: 'zh',
  59. showinfobar: false,
  60. showstatisticBar: false,
  61. hook: {
  62. cellMousedown: (cell, position, sheet) => {
  63. onClickCell && onClickCell(cell, position, sheet);
  64. },
  65. cellPasteBefore: cell => {
  66. console.log(cell);
  67. if (!cell) return;
  68. if (cell.cid) delete cell.cid;
  69. if (cell.bg == DIFF_COLOR || cell.bg == ADD_COLOR) {
  70. delete cell.bg;
  71. }
  72. },
  73. },
  74. };
  75. if (version) {
  76. option = {
  77. ...option,
  78. allowUpdate: true,
  79. gridKey: version.id,
  80. loadUrl: `/api/v1/purchase/record/sheet?gridKey=${version.id}&JWT-TOKEN=${token}`,
  81. updateUrl: `ws://47.96.12.136:8896/api/v1/ws?id=${version.id}&sid=1&JWT-TOKEN=${token}`,
  82. getUser,
  83. };
  84. } else if (data && data.length > 0) {
  85. option.data = JSON.parse(JSON.stringify(data));
  86. option.data.forEach(item => {
  87. if (item.celldata) {
  88. item.celldata.forEach(cell => {
  89. // 生成uuid
  90. if (!cell.v.cid) cell.v.cid = this.getUUID();
  91. });
  92. }
  93. // 默认禁止编辑
  94. // item.config.authority = { sheet: true, hintText };
  95. });
  96. } else {
  97. // 默认sheet页数据
  98. data.data = [
  99. {
  100. name: 'sheet1',
  101. // config: {
  102. // authority: { sheet: true, hintText },
  103. // },
  104. },
  105. ];
  106. }
  107. // this.luckysheet.destroy();
  108. this.luckysheet.create(option);
  109. }
  110. // componentDidUpdate(prevProps) {
  111. // const { data } = this.props;
  112. // if (prevProps.data != data) {
  113. // this.renderSheet(data);
  114. // }
  115. // }
  116. handleLoad() {
  117. const { onLoad } = this.props;
  118. let contentWindow = this.sheetRef.current.contentWindow;
  119. this.luckysheet = contentWindow.luckysheet;
  120. // this.luckysheet = this.luckysheet;
  121. this.renderSheet();
  122. // onLoad && onLoad();
  123. }
  124. selectCell(row, col, order) {
  125. this.luckysheet.setRangeShow({ row: [row, row], column: [col, col] }, { order });
  126. }
  127. toggleSheet(order) {
  128. this.luckysheet.setSheetActive(order);
  129. }
  130. getSheetJson() {
  131. let data = JSON.parse(JSON.stringify(this.luckysheet.toJson()));
  132. data.data.forEach(sheet => {
  133. (sheet.celldata || []).forEach(cell => {
  134. // 添加cid
  135. if (!cell.v.cid) cell.v.cid = this.getUUID();
  136. // 清除比对样式
  137. if (cell.v.bg == DIFF_COLOR || cell.v.bg == ADD_COLOR) {
  138. delete cell.v.bg;
  139. }
  140. });
  141. });
  142. return data;
  143. }
  144. // 切换编辑状态
  145. toggleEdit(edit) {
  146. let luckysheet = this.luckysheet;
  147. if (edit) {
  148. let config = luckysheet.getConfig();
  149. luckysheet.setConfig({
  150. ...config,
  151. authority: { sheet: !edit, hintText },
  152. });
  153. } else {
  154. luckysheet.exitEditMode();
  155. }
  156. }
  157. // 切换比对状态
  158. toggleCompare(isCompare, compareData, callback) {
  159. let luckysheet = this.luckysheet;
  160. let diff = [];
  161. let add = [];
  162. const { onCompareSuccess } = this.props;
  163. // 判断dom是否加载完成
  164. if (!luckysheet) {
  165. setTimeout(() => {
  166. this.toggleCompare(isCompare, compareData, callback);
  167. }, 300);
  168. return;
  169. }
  170. if (isCompare) {
  171. // let currentData = this.luckysheet.toJson();
  172. let currentData = JSON.parse(JSON.stringify(this.props.data));
  173. currentData.forEach((sheet, index) => {
  174. let celldata1 = sheet.celldata;
  175. let celldata2 = compareData[index]?.celldata || [];
  176. celldata1.forEach(item => {
  177. // 不判断空字符串
  178. if (this.isEmpty(item)) return;
  179. var d2Item = celldata2.find(item2 => item2.v.cid == item.v.cid);
  180. if (d2Item && !this.isEmpty(d2Item)) {
  181. // v.ct.s相同,不做处理
  182. if (item.v.ct?.s && JSON.stringify(item.v.ct?.s) == JSON.stringify(d2Item.v.ct?.s))
  183. return;
  184. // v.v相同,不做处理
  185. if (d2Item.v.v == item.v.v) return;
  186. // 内容不同,标记diff颜色
  187. diff.push({
  188. ...item,
  189. sheetOrder: index,
  190. });
  191. item.v.bg = DIFF_COLOR;
  192. // luckysheet.setCellFormat(item.r, item.c, 'bg', DIFF_COLOR);
  193. } else {
  194. // 找不到同cid的单元格,标记add颜色
  195. add.push({
  196. ...item,
  197. sheetOrder: index,
  198. });
  199. item.v.bg = ADD_COLOR;
  200. // luckysheet.setCellFormat(item.r, item.c, 'bg', ADD_COLOR);
  201. }
  202. });
  203. });
  204. // console.log(currentData);
  205. this.renderSheet(currentData);
  206. // luckysheet.refresh()
  207. } else {
  208. this.renderSheet(this.props.data);
  209. }
  210. this.updateCell = {
  211. diff,
  212. add,
  213. };
  214. callback && callback(this.updateCell);
  215. }
  216. isEmpty(item) {
  217. return (item?.v?.v ?? '') === '' && !item?.v?.ct?.s;
  218. }
  219. mergeExcl(mergeData = []) {
  220. let currentData = this.luckysheet.toJson();
  221. let luckysheet = this.luckysheet;
  222. // let add = [];
  223. currentData.data.forEach((sheet, index) => {
  224. if (!mergeData[index]) return;
  225. let celldata1 = sheet.celldata;
  226. let celldata2 = mergeData[index].celldata;
  227. celldata2.forEach(item => {
  228. var d2Item = celldata1.find(item2 => item2.v.cid == item.v.cid);
  229. if (!d2Item) {
  230. delete item.v.v.bg;
  231. // 将新增项添加至当前文档
  232. luckysheet.setCellValue(item.r, item.c, item.v);
  233. // 记录新增
  234. // add.push(item);
  235. }
  236. });
  237. });
  238. // let celldata1 = currentData.data[0].celldata;
  239. // let celldata2 = mergeData.data[0].celldata;
  240. // this.add = add;
  241. // return add;
  242. }
  243. /**
  244. * 导入excl
  245. * @param {*} files input:file的evt.target.files
  246. * @returns
  247. */
  248. uploadExcel(files, callback) {
  249. if (files == null || files.length == 0) {
  250. return;
  251. }
  252. let name = files[0].name;
  253. let suffixArr = name.split('.'),
  254. suffix = suffixArr[suffixArr.length - 1];
  255. if (suffix != 'xlsx') {
  256. alert('Currently only supports the import of xlsx files');
  257. message.error('只支持xlsx格式的文件!');
  258. return;
  259. }
  260. LuckyExcel.transformExcelToLucky(files[0], (exportJson, luckysheetfile) => {
  261. if (exportJson.sheets == null || exportJson.sheets.length == 0) {
  262. message.error('读取xlsx文件失败!');
  263. return;
  264. }
  265. // this.luckysheet.destroy();
  266. // 同步当前文档内容
  267. let data = this.props.data;
  268. exportJson.sheets.forEach((sheet, index) => {
  269. if (!data || !data[index]) return;
  270. sheet.celldata.forEach(cell => {
  271. if (this.isEmpty(cell)) return;
  272. // return (item.v.v ?? '') === '' && !item.v.ct?.s;
  273. let dCell = (data[index].celldata || []).find(dCell => {
  274. return dCell.r == cell.r && dCell.c == cell.c;
  275. });
  276. if (this.isEmpty(dCell)) return;
  277. // 判断v.ct是否相同
  278. // if (cell?.v?.ct?.s && dCell.v.ct?.s && cell.v.ct?.s.join('') != dCell.v.ct?.s.join('')) return;
  279. if (cell?.v?.ct?.s && dCell.v.ct?.s) {
  280. if (cell.v.ct?.s.join('') != dCell.v.ct?.s.join('')) return;
  281. let cellS = cell.v.ct.s;
  282. let dCellS = dCell.v.ct.s;
  283. let isEqul = cellS.every((cur, idx) => {
  284. return JSON.stringify(cur) === JSON.stringify(dCellS[idx]);
  285. });
  286. if (!isEqul) return;
  287. }
  288. // 判断v.v是否相同
  289. if (cell?.v?.v && dCell.v.v != cell.v.v) return;
  290. // 内容相同则复制cid
  291. cell.cid = dCell.cid;
  292. });
  293. });
  294. this.renderSheet(exportJson.sheets);
  295. callback && callback();
  296. });
  297. }
  298. // 根据url导入excl
  299. // selectExcel(item) {
  300. // const {value,name} = item
  301. // if (value == '') {
  302. // return;
  303. // }
  304. // LuckyExcel.transformExcelToLuckyByUrl(value, name, (exportJson, luckysheetfile) => {
  305. // if (exportJson.sheets == null || exportJson.sheets.length == 0) {
  306. // alert(
  307. // 'Failed to read the content of the excel file, currently does not support xls files!'
  308. // );
  309. // return;
  310. // }
  311. // this.luckysheet.destroy();
  312. // this.luckysheet.create({
  313. // container: 'luckysheet', //luckysheet is the container id
  314. // showinfobar: false,
  315. // data: exportJson.sheets,
  316. // title: exportJson.info.name,
  317. // userInfo: exportJson.info.name.creator,
  318. // });
  319. // });
  320. // }
  321. downloadExcel(checkValue) {
  322. let resultList = [];
  323. console.log(this.luckysheet.getAllSheets());
  324. let currentData = this.luckysheet.getAllSheets();
  325. currentData.forEach(sheet => {
  326. let data = sheet.data;
  327. let celldata = sheet.celldata;
  328. //获取当前导出列的数组
  329. let colList = [];
  330. data[0]?.forEach((rowOneItem, colIdx) => {
  331. if (rowOneItem && checkValue.indexOf(rowOneItem.v) !== -1) {
  332. colList.indexOf(colIdx) == -1 ? colList.push(colIdx) : true;
  333. }
  334. });
  335. //处理data
  336. const newData = [];
  337. data.forEach(item => {
  338. if (item !== null) {
  339. let arr = item.filter((cur, idx) => {
  340. return item && colList.includes(idx);
  341. });
  342. // let len = item.length - arr.length
  343. // let nullList = new Array(len).fill(null);
  344. // let rowList = arr.concat(nullList);
  345. newData.push(arr);
  346. }
  347. });
  348. sheet.data = newData;
  349. //消除空列后都列下标
  350. let newColIdxList = colList.map((cur, idx) => {
  351. return idx;
  352. });
  353. //处理celldata
  354. const newCellData = [];
  355. celldata.forEach(item => {
  356. let idx = colList.indexOf(item.c);
  357. if (idx !== -1) {
  358. item.c = newColIdxList[idx];
  359. newCellData.push(item);
  360. }
  361. });
  362. sheet.celldata = newCellData;
  363. });
  364. exportExcel(currentData, '下载');
  365. }
  366. render() {
  367. return (
  368. <iframe
  369. onLoad={e => {
  370. this.handleLoad(e);
  371. }}
  372. ref={this.sheetRef}
  373. src="/luckysheet.html"
  374. ></iframe>
  375. );
  376. }
  377. }
  378. export default LuckySheet;