WPS表格 JS宏常用工具函数分享

在宏开发过程中我们会有一些非常常见的操作,WPS并没有为我们提供原生的方法,因此,我将我开发过程中经常使用到的一些操作封装成了函数供大家直接使用。代码不一定是最佳实践,水平有限敬请谅解。

  1. 将列名转化为数字,将数字转化为列名

//列名数字互相转换
//列名转化为数字
/**
* @param {string} col 
* @returns {number}
*/
function col2Num(col){
    const alphabeta = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    let num = 0
    for(let i = col.length - 1;i>=0;i --){
        const char = col[i]
        let index = alphabeta.split('').findIndex(item => item === char)
        num += (index+1) * (26 ** (col.length - 1 - i))
    }
    return num
}
//数字转化为列名
/**
* @param {number} num 
* @returns {string}
*/
function num2Col(num){
    const alphabeta = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    let col = []
    while(num >= 1){
        num --
        if(!num){
            col.unshift('A')
            continue
        }
        let word = num % 26
        col.unshift(alphabeta[word])
        num = Math.floor(num / 26)
    }
    return col.join('')
}

  1. 找到表格的最后一行

我们传入对表单的引用(sheet对象),判断基于的列(支持通过多列判断,返回这些列最底部的一个行号) 以及开始扫描的行号,函数就会返回该表格存在数据的最后一个行号。需要注意的是,该函数要求表格数据一行一行之间没有空白行,否则会将该空白行作为最后表格末尾。

/**
 * 
 * @param {sheet} sht 
 * @param {string[]} cols 
 * @param {number} begin 
 * @returns 
 */
function touchBottom(sht,cols,begin = 1){
    let base = 1
    while(base < begin) base *= 2
    function ifEmpty(row){
        let empty = true
        for(const col of cols){
            if(sht.Range(`${col}${row}`).Value2){ empty = false; break}
        }
        return empty
    }
    while(!ifEmpty(base)) base *= 2
    let start = base - 1 ? base / 2 : 1
    let move = start
    while(start < base - 1){
        if(!ifEmpty(start)){
            if(!ifEmpty(start + 1)){
                move = start
                start = (start + base) / 2
            }else{
                break
            }
        }else{
            base = start
            start = move
        }
    }
    return start
}

例如,我希望从第5行开始查询sht1表单,找到它A列的最后一条数据的行号,那么我们调用:

touchBottom(sht1, ["A"], 5)

  1. 检测单元格是否为合并单元格的第一格

该函数可以通过传入的表单引用,列名,行号判断该单元格是否为合并单元格的第一格。

合并单元格的数据通常存储于合并单元格的最上方单元格中,该函数可以很方便地应用于需要遍历存在合并单元格的表格的情况。

/**
 * 
 * @param {sheet} sht 
 * @param {string} col 
 * @param {number|string} row 
 * @returns 
 */
function ifCellIsFirstCell(sht,col,row){
    return row === 1 || !(sht.Range(`${col}${row - 1}:${col}${row}`).MergeCells)
}

  1. 在进行数据写入操作时,关闭数据刷新和自动计算功能,数据写入操作完毕后在打开,以大幅提升JS宏执行速度

 //运行函数时暂停wps刷新机制和自动计算以加快运行速度
function fastExec(callback,...args){
    Application.Calculation = xlCalculationManual
    Application.ScreenUpdating = false
    const output = callback(...args)
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = true
    return output
}

  1. 用于表格数据处理的工具类

//数据表处理
class DataHandler{
    /**
     * @param {Sheet} raw 
     * @param {number} titleLine 
     */
    constructor(raw,titleLine = 1,defaultMaxCol='Z'){
        Object.assign(this,{raw,titleLine,len:touchBottom(raw,["A"]),defaultMaxCol})
        this.filteredData = null
        this.filteredDataCols = null
    }
    /**
     * @param {string[]} cols 
     * @param {function} condition 
     * @param {object} params
     */
    //params = {formatTime:string,...} formatTime:日期所在列; defaultMaxCol:截取表格的最大列
    //condition function(row:any[]):boolean
    filter(cols,condition = () => true, params={}){
        ////---处理params额外参数---
        //格式化日期时间
        if(params.formatTime) this.raw.Range(`${params.formatTime}:${params.formatTime}`).TextToColumns(Range(`${params.formatTime}1`), xlDelimited, xlTextQualifierDoubleQuote, true, true, false, false, true, false, undefined, Array(Array(1, 5)), undefined, undefined, true)
        //设定默认最大列
        let defaultMaxCol = params.defaultMaxCol || 'Z'
        let data = this.raw.Range(`A1:${defaultMaxCol}${this.len}`).Value2

        ////---筛选表格---
        //按标题筛选列, 筛选结果列遵从参数cols的顺序
        let filteredData = []
        const titleMap = {}
        for(const t of cols) {
            let titleIndex = data[0].findIndex(i => i === t)
            if(titleIndex === -1) throw new Error(`筛选数据表时发现所提供的数据表缺少字段:${t}!`)
            else titleMap[t] = titleIndex
        }
        data.shift()
        data.forEach(row => {
            const filteredRow = cols.map( t => row[titleMap[t]])
            filteredData.push(filteredRow)
        })
        data = null
        //按条件筛选行,row代表每一行数据,condition回调函数对每一行数据进行判断,留下符合条件的数据
        filteredData = filteredData.filter(row => condition(row))
        this.filteredData = filteredData
        this.filteredDataCols = cols
        return filteredData
    }
}

使用时,我们先使用表单的引用实例化DataHandler类:

const dh = new DataHandler(sheetRef)

默认以表格第一行作为标题行,引用范围默认为A-Z列。如需修改,请在实例化的时候传入对应参数。

例如,我希望以第二行为标题行,引用至AZ列:

const dh = new DataHandler(sheetRef, 2, "AZ")

实例化完成后,我们需要筛选表格,调用实例的filter方法,我们可以非常方便地筛选表格数据:

const data = dh.filter(["国家","仓库","产品","数量"], row => row[1] === "仓库A") 
//只保留表格的 "国家","仓库","产品","数量" 4列,并筛选仓库名为“仓库A”的记录

浙江省
浏览 439
收藏
6
分享
6 +1
2
+1
全部评论 2
 
code4101
第1个,云文档AirScript1.0是有两个隐藏函数能用的(as2.0没了): console.log(Base26To10('ABC')) console.log(Base10To26(233)) 如果自己实现,也可以"取巧"利用表格本身的功能: console.log(Columns('ABC').Column) console.log(Columns(233).Address(false, false)) # 需要对取到的结果再加个正则取字母前缀就行 第2个功能的实现,借助.End(xlUp)或.End(xlDown)应该会更简单。 ws.Cells(1, col).End(xlDown) 第3~5的功能,有不少启发和收获,感谢分享 关于怎么结构定位表格和进行数据处理,我也有写过一套代码,有兴趣可以看看:https://bbs.wps.cn/topic/45636
· 北京
回复
消えた夢に
感谢大佬分享列名数字互相转化的新思路❤️,试了试第二个方法在wps表格里面也可以跑通。 不过对于查找表格最后一行的方法,在普通表格中似乎行不通,可能wps只在airscript中支持了这样的方法☹️
· 浙江省
回复