当前位置:   article > 正文

全网最全luckysheet协同编辑导出功能,包含单元格样式、超链接、数据验证、条件格式、图片导出、批注、数据透视图等等复杂功能的导出

luckysheet

前言:

        最近公司准备研究使用luckySheet,为了开源节流,在开掉了前端大佬的情况下,这苦差事儿落在了我这个前端菜鸟身上。无奈,只能打开luckysheet官网,仔细研读文档,并照着敲了个在线协同的demo。本以为这事儿算是结束,谁知,公司来了新需求,要做导入导出功能,又是一番查找资料后,决定先做导出,哈哈哈,别问为什么,问就是因为导出相关的文档丰富,可操作性强。

废话结束,进入正题:

        1. 在官网找到了导出代码,但只有基础的功能,标题所描述的复杂功能的导出并没有实现,于是我就拿下来进行二次开发,链接:使用exceljs导出luckysheet表格_excel 转 luckysheet-CSDN博客

        2. 用到导出库excejs和file-saver 

  1. //安装exceljs
  2. npm i exceljs --save
  3. //安装文件保存的库
  4. npm i file-saver --save

        3. 二次开发

        export.js
  1. const Excel = require('exceljs')
  2. import FileSaver from 'file-saver'
  3. import { setConditions } from './export/setConditions'
  4. import { setDataValidation } from './export/setDataValidation'
  5. import { createCellRange } from './export/utils'
  6. export var exportExcel = function(luckysheet, value) {
  7. console.log(luckysheet,'sheet数组')
  8. console.log(luckysheet[0].dataVerification,'sheet1中具体操作')
  9. // 参数为luckysheet.getluckysheetfile()获取的对象
  10. // 1.创建工作簿,可以为工作簿添加属性
  11. const workbook = new Excel.Workbook()
  12. // 2.创建表格,第二个参数可以配置创建什么样的工作表
  13. if (Object.prototype.toString.call(luckysheet) === '[object Object]') {
  14. luckysheet = [luckysheet]
  15. }
  16. //遍历sheet,将luckysheet的sheet转换成excel的sheet
  17. luckysheet.forEach(async function(thesheet) {
  18. // thesheet为sheet对象数据
  19. if (thesheet.data.length === 0) return true
  20. const worksheet = workbook.addWorksheet(thesheet.name)
  21. const merge = (thesheet.config && thesheet.config.merge) || {}
  22. const borderInfo = (thesheet.config && thesheet.config.borderInfo) || {}
  23. // 3.设置导出操作
  24. setStyleAndValue(thesheet.data, worksheet)
  25. setMerge(merge, worksheet)
  26. setBorder(borderInfo, worksheet)
  27. setImages(thesheet, worksheet, workbook);
  28. setHyperlink(thesheet.hyperlink,worksheet)
  29. setFrozen(thesheet.frozen,worksheet)
  30. setConditions(thesheet.luckysheet_conditionformat_save,worksheet)
  31. setFilter(thesheet.filter_select,worksheet)
  32. setDataValidation(thesheet.dataVerification,worksheet)
  33. //开启显示数据透视表
  34. if(thesheet.isPivotTable){
  35. worksheet.pivotTables = true
  36. let rmax = 0
  37. let cmax = 0
  38. //得到行与列的最大值
  39. thesheet.celldata.forEach(itemCell=>{
  40. if(rmax<itemCell.r) rmax = itemCell.r
  41. if(cmax<itemCell.c) cmax = itemCell.c
  42. })
  43. // 循环遍历添加边框
  44. for(let i=0;i<=rmax;i++){
  45. for(let j=0;j<=cmax;j++){
  46. // 添加边框
  47. worksheet.getCell(i+1,j+1).border = {
  48. top: {style:'thin'},
  49. left: {style:'thin'},
  50. bottom: {style:'thin'},
  51. right: {style:'thin'}
  52. }
  53. }
  54. }
  55. }
  56. return true
  57. })
  58. // return
  59. // 4.写入 buffer
  60. const buffer = workbook.xlsx.writeBuffer().then(data => {
  61. const blob = new Blob([data], {
  62. type: 'application/vnd.ms-excel;charset=utf-8'
  63. })
  64. //保存文件
  65. FileSaver.saveAs(blob, `${value}.xlsx`)
  66. })
  67. return buffer
  68. }
  69. //设置合并数据
  70. var setMerge = function(luckyMerge = {}, worksheet) {
  71. const mergearr = Object.values(luckyMerge)
  72. mergearr.forEach(function(elem) {
  73. // elem格式:{r: 0, c: 0, rs: 1, cs: 2}
  74. // 按开始行,开始列,结束行,结束列合并(相当于 K10:M12)
  75. worksheet.mergeCells(
  76. elem.r + 1,
  77. elem.c + 1,
  78. elem.r + elem.rs,
  79. elem.c + elem.cs
  80. )
  81. })
  82. }
  83. //重新设置边框
  84. var setBorder = function(luckyBorderInfo, worksheet) {
  85. if (!Array.isArray(luckyBorderInfo)) return
  86. luckyBorderInfo.forEach(function(elem) {
  87. // 现在只兼容到borderType 为range的情况
  88. if (elem.rangeType === 'range') {
  89. let border = borderConvert(elem.borderType, elem.style, elem.color)
  90. let rang = elem.range[0]
  91. let row = rang.row
  92. let column = rang.column
  93. for (let i = row[0] + 1; i < row[1] + 2; i++) {
  94. for (let y = column[0] + 1; y < column[1] + 2; y++) {
  95. worksheet.getCell(i, y).border = border
  96. }
  97. }
  98. }
  99. if (elem.rangeType === 'cell') {
  100. // col_index: 2
  101. // row_index: 1
  102. // b: {
  103. // color: '#d0d4e3'
  104. // style: 1
  105. // }
  106. const { col_index, row_index } = elem.value
  107. const borderData = Object.assign({}, elem.value)
  108. delete borderData.col_index
  109. delete borderData.row_index
  110. let border = addborderToCell(borderData, row_index, col_index)
  111. worksheet.getCell(row_index + 1, col_index + 1).border = border
  112. }
  113. })
  114. }
  115. // 设置单元格样式和值
  116. var setStyleAndValue = function (cellArr, worksheet) {
  117. if (!Array.isArray(cellArr)) return;
  118. cellArr.forEach(function (row, rowid) {
  119. const dbrow = worksheet.getRow(rowid + 1);
  120. //设置单元格行高,默认乘以0.8倍
  121. dbrow.height = luckysheet.getRowHeight([rowid])[rowid] * 0.8;
  122. row.every(function (cell, columnid) {
  123. if (!cell) return true;
  124. if (rowid == 0) {
  125. const dobCol = worksheet.getColumn(columnid + 1);
  126. //设置单元格列宽除以8
  127. dobCol.width = luckysheet.getColumnWidth([columnid])[columnid] / 8;
  128. }
  129. let fill = fillConvert(cell.bg);
  130. let font = fontConvert(
  131. cell.ff||'Times New Roman',
  132. cell.fc,
  133. cell.bl,
  134. cell.it,
  135. cell.fs,
  136. cell.cl,
  137. cell.ul
  138. );
  139. let alignment = alignmentConvert(cell.vt, cell.ht, cell.tb, cell.tr);
  140. let value;
  141. var v = "";
  142. if (cell.ct && cell.ct.t == "inlineStr") {
  143. var s = cell.ct.s;
  144. s.forEach(function (val, num) {
  145. v += val.v;
  146. });
  147. } else {
  148. //导出后取显示值
  149. v = cell.m;
  150. }
  151. if (cell.f) {
  152. value = { formula: cell.f, result: v };
  153. } else {
  154. value = v;
  155. }
  156. let target = worksheet.getCell(rowid + 1, columnid + 1);
  157. //添加批注
  158. if(cell.ps){
  159. let ps = cell.ps
  160. target.note = ps.value
  161. }
  162. //单元格填充
  163. target.fill = fill;
  164. //单元格字体
  165. target.font = font;
  166. target.alignment = alignment;
  167. target.value = value;
  168. return true;
  169. });
  170. });
  171. };
  172. /**
  173. * *数据验证
  174. */
  175. // var setDataValidation = function(verify,worksheet){
  176. // if(!verify) return
  177. // for (const key in verify) {
  178. // const row_col = key.split('_')
  179. // let cell =worksheet.getCell(Number(row_col[0])+1,Number(row_col[1])+1)
  180. // }
  181. // }
  182. /**
  183. * *数据透视图
  184. */
  185. /**
  186. * *筛选导出
  187. */
  188. var setFilter = function(filter,worksheet){
  189. if(!filter) return
  190. worksheet.autoFilter = createCellRange(filter.row,filter.column)
  191. }
  192. /**
  193. * *冻结视图
  194. */
  195. var setFrozen = function(frozen,worksheet){
  196. //不存在冻结或取消冻结,则不执行后续代码
  197. if(!frozen||frozen.type=='cancel') return
  198. //执行冻结操作代码
  199. let views = []
  200. switch (frozen.type) {
  201. //冻结首行
  202. case 'row':
  203. views = [
  204. {state: 'frozen', xSplit: 0, ySplit: 1}
  205. ];
  206. break;
  207. //冻结首列
  208. case 'column':
  209. views = [
  210. {state: 'frozen', xSplit: 1, ySplit: 0}
  211. ];
  212. break;
  213. //冻结首行首列
  214. case 'both':
  215. views = [
  216. {state: 'frozen', xSplit: 1, ySplit: 1}
  217. ];
  218. break;
  219. //冻结行至选区
  220. case 'rangeRow':
  221. views = [
  222. {state: 'frozen', xSplit: 0, ySplit: frozen.range.row_focus+1}
  223. ];
  224. break;
  225. //冻结列至选区
  226. case 'rangeColumn':
  227. views = [
  228. {state: 'frozen', xSplit: frozen.range.column_focus+1, ySplit: 0}
  229. ];
  230. break;
  231. //冻结至选区
  232. case 'rangeBoth':
  233. views = [
  234. {state: 'frozen', xSplit: frozen.range.column_focus+1, ySplit: frozen.range.row_focus+1}
  235. ];
  236. break;
  237. }
  238. worksheet.views = views
  239. }
  240. /**
  241. * *设置超链接
  242. */
  243. var setHyperlink = function(hyperlink,worksheet){
  244. if (!hyperlink) return;
  245. for (const key in hyperlink) {
  246. const row_col = key.split('_')
  247. let cell =worksheet.getCell(Number(row_col[0])+1,Number(row_col[1])+1)
  248. let font = cell.style.font
  249. //设置导出后超链接的样式
  250. // cell.font= fontConvert(font.name,'#0000ff',font.bold,font.italic,font.size,font.strike,true)
  251. cell.font= fontConvert(font.name,'#0000ff',0,0,font.size,0,true)
  252. if(hyperlink[key].linkType=="external"){
  253. //外部链接
  254. cell.value = {
  255. text: cell.value,
  256. hyperlink: hyperlink[key].linkAddress,
  257. tooltip: hyperlink[key].linkTooltip
  258. }
  259. }else{
  260. // 内部链接
  261. const linkArr = hyperlink[key].linkAddress.split('!')
  262. let hyper = '#\\'+linkArr[0]+'\\'+'!'+linkArr[1]
  263. cell.value = {
  264. text: cell.value,
  265. hyperlink:hyper,
  266. tooltip: hyperlink[key].linkTooltip
  267. }
  268. }
  269. }
  270. }
  271. /**
  272. * *设置图片
  273. */
  274. var setImages = function (thesheet, worksheet, workbook) {
  275. let {
  276. images,//图片对象或者数组
  277. visibledatacolumn, //所有行的位置
  278. visibledatarow, //所有列的位置
  279. } = { ...thesheet };
  280. if (typeof images != "object") return;
  281. for (let key in images) {
  282. // 通过 base64 将图像添加到工作簿
  283. const myBase64Image = images[key].src;
  284. //开始行 开始列 结束行 结束列
  285. const item = images[key];
  286. const imageId = workbook.addImage({
  287. base64: myBase64Image,
  288. extension: "png",
  289. });
  290. const col_st = getImagePosition(item.default.left, visibledatacolumn);
  291. const row_st = getImagePosition(item.default.top, visibledatarow);
  292. //模式1,图片左侧与luckysheet位置一样,像素比例保持不变,但是,右侧位置可能与原图所在单元格不一致
  293. worksheet.addImage(imageId, {
  294. tl: { col: col_st, row: row_st },
  295. ext: { width: item.default.width, height: item.default.height },
  296. });
  297. //模式2,图片四个角位置没有变动,但是图片像素比例可能和原图不一样
  298. // const w_ed = item.default.left+item.default.width;
  299. // const h_ed = item.default.top+item.default.height;
  300. // const col_ed = getImagePosition(w_ed,visibledatacolumn);
  301. // const row_ed = getImagePosition(h_ed,visibledatarow);
  302. // worksheet.addImage(imageId, {
  303. // tl: { col: col_st, row: row_st},
  304. // br: { col: col_ed, row: row_ed},
  305. // });
  306. }
  307. };
  308. //获取图片在单元格的位置
  309. var getImagePosition = function (num, arr) {
  310. let index = 0;
  311. let minIndex;
  312. let maxIndex;
  313. for (let i = 0; i < arr.length; i++) {
  314. if (num < arr[i]) {
  315. index = i;
  316. break;
  317. }
  318. }
  319. if (index == 0) {
  320. minIndex = 0;
  321. maxIndex = 1;
  322. return Math.abs((num - 0) / (arr[maxIndex] - arr[minIndex])) + index;
  323. } else if (index == arr.length - 1) {
  324. minIndex = arr.length - 2;
  325. maxIndex = arr.length - 1;
  326. } else {
  327. minIndex = index - 1;
  328. maxIndex = index;
  329. }
  330. let min = arr[minIndex];
  331. let max = arr[maxIndex];
  332. let radio = Math.abs((num - min) / (max - min)) + index;
  333. return radio;
  334. };
  335. //单元格背景填充色处理
  336. var fillConvert = function (bg) {
  337. if (!bg) {
  338. return null;
  339. // return {
  340. // type: 'pattern',
  341. // pattern: 'solid',
  342. // fgColor:{argb:'#ffffff'.replace('#','')}
  343. // }
  344. }
  345. bg = bg.indexOf("rgb") > -1 ? rgb2hex(bg) : bg;
  346. let fill = {
  347. type: "pattern",
  348. pattern: "solid",
  349. fgColor: { argb: bg.replace("#", "") },
  350. };
  351. return fill;
  352. };
  353. //转换颜色
  354. var rgb2hex = function (rgb) {
  355. if (rgb.charAt(0) == "#") {
  356. return rgb;
  357. }
  358. var ds = rgb.split(/\D+/);
  359. var decimal = Number(ds[1]) * 65536 + Number(ds[2]) * 256 + Number(ds[3]);
  360. return "#" + zero_fill_hex(decimal, 6);
  361. function zero_fill_hex(num, digits) {
  362. var s = num.toString(16);
  363. while (s.length < digits) s = "0" + s;
  364. return s;
  365. }
  366. };
  367. //字体转换处理
  368. var fontConvert = function (
  369. ff = 0,
  370. fc = "#000000",
  371. bl = 0,
  372. it = 0,
  373. fs = 10,
  374. cl = 0,
  375. ul = 0
  376. ) {
  377. // luckysheet:ff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线)
  378. const luckyToExcel = {
  379. 0: "微软雅黑",
  380. 1: "宋体(Song)",
  381. 2: "黑体(ST Heiti)",
  382. 3: "楷体(ST Kaiti)",
  383. 4: "仿宋(ST FangSong)",
  384. 5: "新宋体(ST Song)",
  385. 6: "华文新魏",
  386. 7: "华文行楷",
  387. 8: "华文隶书",
  388. 9: "Arial",
  389. 10: "Times New Roman",
  390. 11: "Tahoma ",
  391. 12: "Verdana",
  392. num2bl: function (num) {
  393. return num === 0||false ? false : true;
  394. },
  395. };
  396. // let color = fc ? "" : (fc + "").indexOf("rgb") > -1 ? util.rgb2hex(fc) : fc;
  397. // let color = fc ? fc : (fc + "").indexOf("rgb") > -1 ? util.rgb2hex(fc) : fc;
  398. let font = {
  399. name: ff,
  400. family: 1,
  401. size: fs,
  402. color: { argb: fc.replace("#", "") },
  403. bold: luckyToExcel.num2bl(bl),
  404. italic: luckyToExcel.num2bl(it),
  405. underline: luckyToExcel.num2bl(ul),
  406. strike: luckyToExcel.num2bl(cl),
  407. };
  408. return font;
  409. };
  410. //对齐转换
  411. var alignmentConvert = function(
  412. vt = 'default',
  413. ht = 'default',
  414. tb = 'default',
  415. tr = 'default'
  416. ) {
  417. // luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转)
  418. const luckyToExcel = {
  419. vertical: {
  420. 0: 'middle',
  421. 1: 'top',
  422. 2: 'bottom',
  423. default: 'top'
  424. },
  425. horizontal: {
  426. 0: 'center',
  427. 1: 'left',
  428. 2: 'right',
  429. default: 'left'
  430. },
  431. wrapText: {
  432. 0: false,
  433. 1: false,
  434. 2: true,
  435. default: false
  436. },
  437. textRotation: {
  438. 0: 0,
  439. 1: 45,
  440. 2: -45,
  441. 3: 'vertical',
  442. 4: 90,
  443. 5: -90,
  444. default: 0
  445. }
  446. }
  447. let alignment = {
  448. vertical: luckyToExcel.vertical[vt],
  449. horizontal: luckyToExcel.horizontal[ht],
  450. wrapText: luckyToExcel.wrapText[tb],
  451. textRotation: luckyToExcel.textRotation[tr]
  452. }
  453. return alignment
  454. }
  455. //边框转换
  456. var borderConvert = function(borderType, style = 1, color = '#000') {
  457. // 对应luckysheet的config中borderinfo的的参数
  458. if (!borderType) {
  459. return {}
  460. }
  461. const luckyToExcel = {
  462. type: {
  463. 'border-all': 'all',
  464. 'border-top': 'top',
  465. 'border-right': 'right',
  466. 'border-bottom': 'bottom',
  467. 'border-left': 'left'
  468. },
  469. style: {
  470. 0: 'none',
  471. 1: 'thin',
  472. 2: 'hair',
  473. 3: 'dotted',
  474. 4: 'dashDot', // 'Dashed',
  475. 5: 'dashDot',
  476. 6: 'dashDotDot',
  477. 7: 'double',
  478. 8: 'medium',
  479. 9: 'mediumDashed',
  480. 10: 'mediumDashDot',
  481. 11: 'mediumDashDotDot',
  482. 12: 'slantDashDot',
  483. 13: 'thick'
  484. }
  485. }
  486. let template = {
  487. style: luckyToExcel.style[style],
  488. color: { argb: color.replace('#', '') }
  489. }
  490. let border = {}
  491. if (luckyToExcel.type[borderType] === 'all') {
  492. border['top'] = template
  493. border['right'] = template
  494. border['bottom'] = template
  495. border['left'] = template
  496. } else {
  497. border[luckyToExcel.type[borderType]] = template
  498. }
  499. return border
  500. }
  501. //向单元格添加边框
  502. function addborderToCell(borders, row_index, col_index) {
  503. let border = {}
  504. const luckyExcel = {
  505. type: {
  506. l: 'left',
  507. r: 'right',
  508. b: 'bottom',
  509. t: 'top'
  510. },
  511. style: {
  512. 0: 'none',
  513. 1: 'thin',
  514. 2: 'hair',
  515. 3: 'dotted',
  516. 4: 'dashDot', // 'Dashed',
  517. 5: 'dashDot',
  518. 6: 'dashDotDot',
  519. 7: 'double',
  520. 8: 'medium',
  521. 9: 'mediumDashed',
  522. 10: 'mediumDashDot',
  523. 11: 'mediumDashDotDot',
  524. 12: 'slantDashDot',
  525. 13: 'thick'
  526. }
  527. }
  528. for (const bor in borders) {
  529. if (borders[bor].color.indexOf('rgb') === -1) {
  530. border[luckyExcel.type[bor]] = {
  531. style: luckyExcel.style[borders[bor].style],
  532. color: { argb: borders[bor].color.replace('#', '') }
  533. }
  534. } else {
  535. border[luckyExcel.type[bor]] = {
  536. style: luckyExcel.style[borders[bor].style],
  537. color: { argb: borders[bor].color }
  538. }
  539. }
  540. }
  541. return border
  542. }

        4. 涉及到的其他文件 

        setConditions.js
  1. /**
  2. * *条件格式设置
  3. */
  4. import { createCellRange } from './utils'
  5. export const setConditions = function(conditions,worksheet){
  6. //条件格式不存在,则不执行后续代码
  7. if(conditions==undefined) return
  8. //循环遍历规则列表
  9. conditions.forEach(item => {
  10. let ruleObj = {
  11. ref:createCellRange(item.cellrange[0].row,item.cellrange[0].column),
  12. rules:[]
  13. }
  14. //lucksheet对应的为----突出显示单元格规则和项目选区规则
  15. if(item.type=='default'){
  16. //excel中type为cellIs的条件下
  17. if(item.conditionName=='equal'||'greaterThan'||'lessThan'||'betweenness'){
  18. ruleObj.rules = setDefaultRules({
  19. type:'cellIs',
  20. operator:item.conditionName=='betweenness'?'between':item.conditionName,
  21. condvalue:item.conditionValue,
  22. colorArr:[item.format.cellColor,item.format.textColor]
  23. })
  24. worksheet.addConditionalFormatting(ruleObj)
  25. }
  26. //excel中type为containsText的条件下
  27. if(item.conditionName=='textContains'){
  28. ruleObj.rules = [
  29. {
  30. type:'containsText',
  31. operator:'containsText', //表示如果单元格值包含在text 字段中指定的值,则应用格式
  32. text:item.conditionValue[0],
  33. style: setStyle([item.format.cellColor,item.format.textColor])
  34. }
  35. ]
  36. worksheet.addConditionalFormatting(ruleObj)
  37. }
  38. //发生日期--时间段
  39. if(item.conditionName=='occurrenceDate'){
  40. ruleObj.rules = [
  41. {
  42. type:'timePeriod',
  43. timePeriod:'today', //表示如果单元格值包含在text 字段中指定的值,则应用格式
  44. style: setStyle([item.format.cellColor,item.format.textColor])
  45. }
  46. ]
  47. worksheet.addConditionalFormatting(ruleObj)
  48. }
  49. //重复值--唯一值
  50. // if(item.conditionName=='duplicateValue'){
  51. // ruleObj.rules = [
  52. // {
  53. // type:'expression',
  54. // formulae:'today', //表示如果单元格值包含在text 字段中指定的值,则应用格式
  55. // style: setStyle([item.format.cellColor,item.format.textColor])
  56. // }
  57. // ]
  58. // worksheet.addConditionalFormatting(ruleObj)
  59. // }
  60. //项目选区规则--top10前多少项的操作
  61. if(item.conditionName=='top10'||'top10%'||'last10'||'last10%'){
  62. ruleObj.rules = [
  63. {
  64. type:'top10',
  65. rank:item.conditionValue[0], //指定格式中包含多少个顶部(或底部)值
  66. percent:item.conditionName=='top10'||'last10'?false:true,
  67. bottom:item.conditionName=='top10'||'top10%'?false:true,
  68. style: setStyle([item.format.cellColor,item.format.textColor])
  69. }
  70. ]
  71. worksheet.addConditionalFormatting(ruleObj)
  72. }
  73. //项目选区规则--高于/低于平均值的操作
  74. if(item.conditionName=='AboveAverage'||'SubAverage'){
  75. ruleObj.rules = [
  76. {
  77. type:'aboveAverage',
  78. aboveAverage:item.conditionName=='AboveAverage'?true:false,
  79. style: setStyle([item.format.cellColor,item.format.textColor])
  80. }
  81. ]
  82. worksheet.addConditionalFormatting(ruleObj)
  83. }
  84. return
  85. }
  86. //数据条
  87. if(item.type == 'dataBar'){
  88. ruleObj.rules = [
  89. {
  90. type:'dataBar',
  91. style:{}
  92. }
  93. ]
  94. worksheet.addConditionalFormatting(ruleObj)
  95. return
  96. }
  97. //色阶
  98. if(item.type == 'colorGradation'){
  99. ruleObj.rules = [
  100. {
  101. type:'colorScale',
  102. color:item.format,
  103. style:{}
  104. }
  105. ]
  106. worksheet.addConditionalFormatting(ruleObj)
  107. return
  108. }
  109. //图标集
  110. if(item.type == 'icons'){
  111. ruleObj.rules = [
  112. {
  113. type:'iconSet',
  114. iconSet:item.format.len
  115. }
  116. ]
  117. worksheet.addConditionalFormatting(ruleObj)
  118. return
  119. }
  120. });
  121. }
  122. /**
  123. *
  124. * @param {
  125. * type:lucketsheet对应的条件导出类型;
  126. * operator:excel对应的条件导入类型;
  127. * condvalue:1个公式字符串数组,返回要与每个单元格进行比较的值;
  128. * colorArr:颜色数组,第一项为单元格填充色,第二项为单元格文本颜色
  129. * } obj
  130. * @returns
  131. */
  132. function setDefaultRules(obj){
  133. let rules = [
  134. {
  135. type:obj.type,
  136. operator:obj.operator,
  137. formulae:obj.condvalue,
  138. style:setStyle(obj.colorArr)
  139. }
  140. ]
  141. return rules
  142. }
  143. /**
  144. *
  145. * @param {颜色数组,第一项为单元格填充色,第二项为单元格文本颜色} colorArr
  146. */
  147. function setStyle(colorArr){
  148. return {
  149. fill: {type: 'pattern', pattern: 'solid', bgColor: {argb: colorArr[0].replace("#", "")}},
  150. font: {color:{ argb: colorArr[1].replace("#", "")},}
  151. }
  152. }
 setDataValidation.js
  1. /**
  2. * *数据验证
  3. */
  4. export const setDataValidation = function (verify, worksheet) {
  5. // 不存在不执行
  6. if (!verify) return;
  7. // 存在则有以下逻辑
  8. for (const key in verify) {
  9. const row_col = key.split("_");
  10. let cell = worksheet.getCell(
  11. Number(row_col[0]) + 1,
  12. Number(row_col[1]) + 1
  13. );
  14. let { type, type2, value1, value2 } = verify[key];
  15. //下拉框--list
  16. if (type == "dropdown") {
  17. cell.dataValidation = {
  18. type: "list",
  19. allowBlank: true,
  20. formulae: [`${value1}`],
  21. };
  22. continue;
  23. }
  24. //整数--whole
  25. if (type == "number_integer") {
  26. cell.dataValidation = {
  27. type: "whole",
  28. operator: setOperator(type2),
  29. showErrorMessage: true,
  30. formulae: value2 ? [Number(value1), Number(value2)] : [Number(value1)],
  31. errorStyle: "error",
  32. errorTitle: "警告",
  33. error: errorMsg(type2, type, value1, value2),
  34. };
  35. continue;
  36. }
  37. //小数-数字--decimal
  38. if (type == "number_decimal" || "number") {
  39. cell.dataValidation = {
  40. type: "decimal",
  41. operator: setOperator(type2),
  42. allowBlank: true,
  43. showInputMessage: true,
  44. formulae: value2 ? [Number(value1), Number(value2)] : [Number(value1)],
  45. promptTitle: "警告",
  46. prompt: errorMsg(type2, type, value1, value2),
  47. };
  48. continue;
  49. }
  50. //长度受控的文本--textLength
  51. if (type == "text_length") {
  52. cell.dataValidation = {
  53. type: "textLength",
  54. operator: setOperator(type2),
  55. showErrorMessage: true,
  56. allowBlank: true,
  57. formulae: value2 ? [Number(value1), Number(value2)] : [Number(value1)],
  58. promptTitle: "错误提示",
  59. prompt: errorMsg(type2, type, value1, value2),
  60. };
  61. continue;
  62. }
  63. //文本的内容--text_content
  64. if (type == "text_content") {
  65. cell.dataValidation = {};
  66. continue;
  67. }
  68. //日期--date
  69. if (type == "date") {
  70. cell.dataValidation = {
  71. type: "date",
  72. operator: setDateOperator(type2),
  73. showErrorMessage: true,
  74. allowBlank: true,
  75. promptTitle: "错误提示",
  76. prompt: errorMsg(type2, type, value1, value2),
  77. formulae: value2
  78. ? [new Date(value1), new Date(value2)]
  79. : [new Date(value1)],
  80. };
  81. continue;
  82. }
  83. //有效性--custom;type2=="phone"/"card"
  84. if (type == "validity") {
  85. // cell.dataValidation = {
  86. // type: 'custom',
  87. // allowBlank: true,
  88. // formulae: [type2]
  89. // };
  90. continue;
  91. }
  92. //多选框--checkbox
  93. if (type == "checkbox") {
  94. cell.dataValidation = {};
  95. continue;
  96. }
  97. }
  98. };
  99. //类型type值为"number"/"number_integer"/"number_decimal"/"text_length"时,type2值可为
  100. function setOperator(type2) {
  101. let transToOperator = {
  102. bw: "between",
  103. nb: "notBetween",
  104. eq: "equal",
  105. ne: "notEqual",
  106. gt: "greaterThan",
  107. lt: "lessThan",
  108. gte: "greaterThanOrEqual",
  109. lte: "lessThanOrEqual",
  110. };
  111. return transToOperator[type2];
  112. }
  113. //数字错误性提示语
  114. function errorMsg(type2, type, value1 = "", value2 = "") {
  115. const tip = "你输入的不是";
  116. const tip1 = "你输入的不是长度";
  117. let errorTitle = {
  118. bw: `${
  119. type == "text_length" ? tip1 : tip
  120. }介于${value1}${value2}之间的${numType(type)}`,
  121. nb: `${
  122. type == "text_length" ? tip1 : tip
  123. }不介于${value1}${value2}之间的${numType(type)}`,
  124. eq: `${type == "text_length" ? tip1 : tip}等于${value1}${numType(type)}`,
  125. ne: `${type == "text_length" ? tip1 : tip}不等于${value1}${numType(
  126. type
  127. )}`,
  128. gt: `${type == "text_length" ? tip1 : tip}大于${value1}${numType(type)}`,
  129. lt: `${type == "text_length" ? tip1 : tip}小于${value1}${numType(type)}`,
  130. gte: `${type == "text_length" ? tip1 : tip}大于等于${value1}${numType(
  131. type
  132. )}`,
  133. lte: `${type == "text_length" ? tip1 : tip}小于等于${value1}${numType(
  134. type
  135. )}`,
  136. //日期
  137. bf: `${type == "text_length" ? tip1 : tip}早于${value1}${numType(type)}`,
  138. nbf: `${type == "text_length" ? tip1 : tip}不早于${value1}${numType(type)}`,
  139. af: `${type == "text_length" ? tip1 : tip}晚于${value1}${numType(
  140. type
  141. )}`,
  142. naf: `${type == "text_length" ? tip1 : tip}不晚于${value1}${numType(
  143. type
  144. )}`,
  145. };
  146. return errorTitle[type2];
  147. }
  148. // 数字类型(整数,小数,十进制数)
  149. function numType(type) {
  150. let num = {
  151. number_integer: "整数",
  152. number_decimal: "小数",
  153. number: "数字",
  154. text_length: "文本",
  155. date:'日期'
  156. };
  157. return num[type];
  158. }
  159. //类型type值为date时
  160. function setDateOperator(type2) {
  161. let transToOperator = {
  162. bw: "between",
  163. nb: "notBetween",
  164. eq: "equal",
  165. ne: "notEqual",
  166. bf: "greaterThan",
  167. nbf: "lessThan",
  168. af: "greaterThanOrEqual",
  169. naf: "lessThanOrEqual",
  170. };
  171. return transToOperator[type2];
  172. }
 utils.js
  1. /**
  2. * *创建单元格所在列的列的字母
  3. * @param {列数的index值} n
  4. * @returns
  5. */
  6. export const createCellPos = function(n) {
  7. let ordA = 'A'.charCodeAt(0)
  8. let ordZ = 'Z'.charCodeAt(0)
  9. let len = ordZ - ordA + 1
  10. let s = ''
  11. while (n >= 0) {
  12. s = String.fromCharCode((n % len) + ordA) + s
  13. n = Math.floor(n / len) - 1
  14. }
  15. return s
  16. }
  17. /**
  18. * *创建单元格范围,期望得到如:A1:D6
  19. * @param {单元格行数组(例如:[0,3])} rowArr
  20. * @param {单元格列数组(例如:[5,7])} colArr
  21. * */
  22. export const createCellRange = function(rowArr,colArr){
  23. const startCell = createCellPos(colArr[0])+(rowArr[0]+1)
  24. const endCell = createCellPos(colArr[1])+(rowArr[1]+1)
  25. return startCell+':'+endCell
  26. }

最后 :

附上exceljs文档的参考地址:

GitHub: GitHub - exceljs/exceljs: Excel Workbook Manager

gitee:exceljs: exceljs不更新了在exceljs@4.3.0基础上优化了内存占用,增加了新的api

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/396052
推荐阅读
相关标签
  

闽ICP备14008679号