wps js宏开发表格数据归类拆分和excel文件合并的xlam加载宏插件

前言

用wps js宏编辑器开发的表格数据拆分与合并的加载宏工具,写好代码把工作簿另存为xlam加载宏文件,然后添加到加载项即可使用。xlam文件你可以理解为没有工作表的工作簿,先看动画演示效果:

主要功能
  1. 向功能区添加宏命令按钮来执行代码

  1. 能在不同的工作簿运行

  1. 数据拆分归类:用户可以指定某列字段进行归类;用户可以设置将归类好的数据分别写入工作表或者工作簿

  1. Excel文件合并:弹出选项窗体保存用户的配置信息,根据用户设置的配置信息进行合并数据

准备文件

老规矩先新建一个xlam文件,然后添加到加载项里再编辑代码

具体操作可以回顾我上一期写的文章:wps js自定义功能区,把写好的宏命令加在excel的功能区

为Excel文件合并_选项插入一个窗体,设计样式如下
wps js宏开发表格数据归类拆分和excel文件合并的xlam加载宏插件_第1张图片

js源码
一、主体代码框架
//工作簿打开事件,触发该事件的时候向功能区添加宏命令
function Workbook_Open() {
    let arr = [
        { title: ['拆分工作表数据', '选项'], jsAction: ['splitSheet', 'splitSheet_option'] },
        { title: ['合并Excel文件', '选项'], jsAction: ['mergeExcelFile', 'mergeExcelFile_option'] }
    ]

    //设置功能区
    arr.forEach((obj) => {
        let bar = Application.CommandBars.Add() //添加CommandBar对象
        bar.Visible = true

        for (let i in obj.title) {
            let btn = bar.Controls.Add(msoControlButton) //添加Button控件
            btn.Caption = obj.title[i]                   //指定Button名称
            btn.OnAction = obj.jsAction[i]               //指定要执行的函数
        }

    })
}

//UserForm1窗体初始化事件,展示用户之前设置好的配置信息
function UserForm1_Initialize() {
    let text = GetSetting('addins', 'excelMergeTools', 'option') //提取注册表信息
    if (text) {
        const option = JSON.parse(text) //JSON字符串转化js对象
        for (let item in option) {
            UserForm1[item].Value = option[item] //设置Control的Value
        }

    } else {
        let pth = Application.DefaultFilePath + '\\' //提取系统默认文档路径
        UserForm1.TextEdit2.Value = pth
        UserForm1.TextEdit1.Value = ''
        UserForm1.OptionButton1.Value = true
    }
}

//UserForm1_CommandButton1点击事件,用于保存用户配置信息
function UserForm1_CommandButton1_Click() {
    //判断用户输入的文件夹路径是否合法
    let prn = /^[a-zA-Z]:((\\)[\S].+\s?)*\\$/
        , str = UserForm1.TextEdit2.Value
    if (!prn.test(str)) return alert('默认保存路径不合法!\n正确范例:C:\\Users\\Administrator\\Desktop\\')

    //读取配置
    const option = {
        TextEdit1: UserForm1.TextEdit1.Value,
        TextEdit2: UserForm1.TextEdit2.Value,
        OptionButton1: UserForm1.OptionButton1.Value,
        OptionButton2: UserForm1.OptionButton2.Value,
        CheckBox1: UserForm1.CheckBox1.Value
    }

    let text = JSON.stringify(option) //js对象转化JSON字符串
    SaveSetting('addins', 'excelMergeTools', 'option', text) //保存到注册表信息
    UserForm1.Close() //卸载窗体
}

//删除配置信息
function deleteSettingTest() {
    DeleteSetting('addins', 'splitSheetTools', 'option')
    DeleteSetting('addins', 'excelMergeTools', 'option')
}

二、功能代码

写好每个按钮对应的处理函数

//拆分工作表数据
function splitSheet() {
    //识别数据区域
    let data = Range('A1').CurrentRegion.Value2 //获取数据
    if (!data) return alert('无法识别数据区域!请确保从A1单元格起有连续非空白的数据区域。')
    
    //获取需要处理的列
    let inf = Application.InputBox('请输入要拆分的列,大小写字母均可\n例如拆分A列数据则输入a', undefined, 'a')
        , prn = /^[a-zA-Z]+$/
        , col = 0
    if (inf == '') return
    if (!prn.test(inf)) return alert('只能输入字母!')
    col = Range(inf + 1).Column - 1 //在data二维数组的的第二维索引
    
    //获取配置信息、数据表头字段
    let splitMode = GetSetting('addins', 'splitSheetTools', 'option')
        , mode = splitMode ? splitMode : '工作表' //拆分模式
        , th = data[0] //表头字段
    
    //处理数据,按用户所选列归类
    const obj = {} //利用对象方法归类
    data.forEach((item, i) => {
        if (i == 0) return
        if (obj[item[col]]) return obj[item[col]] = obj[item[col]].concat([item]) //合并数组
        obj[item[col]] = [th, item]
    })
    
    //指定文件存放路径、获取当前文件名后缀、创建文件夹
    let pth = ActiveWorkbook.Path //获取当前工作簿的路径
        , wbName = ActiveWorkbook.Name
        , suffix = wbName.slice(wbName.lastIndexOf('.'))
    pth = pth ? pth + '\\split' + dateFormat('yyyymmddHHMMSS') + '\\' : Application.DefaultFilePath + '\\split' + dateFormat('yyyymmddHHMMSS') + '\\'
    if (mode != '工作表') MkDir(pth) //创建文件夹
    
    //错误提示
    let wbErr = '重命名工作簿失败,工作簿名称不能包含以下任意字符:\n\\ / : * ? " < > |'
        , shtErr = '重命名工作表失败,当前可能存在相同名称的工作表或者工作表名称无效,合法的工作表名称为:\n1.最多不超过31个字符 \n2.不能包含字符:\\ / ? * [ ] \n3.第一个字符或者最后一个字符不能是单引号 \n4.空字符串'
    
    //循环写入数据
    for (let item in obj) {
        let data = obj[item]
        if (mode == '工作表') {
            //拆分为工作表的操作
            let sht = Worksheets.Add(undefined, ActiveSheet)
            sht.Range('A1').Resize(data.length, data[0].length).Value2 = data
            
            //sht.Name = item
            let ifErr = trycatch(()=>{sht.Name = item}, ()=>{alert(shtErr)}) //错误处理回调函数
            if (ifErr) return
            
            
        } else {
            //拆分为工作簿的操作
            let wb = Workbooks.Add()
                , sht = wb.ActiveSheet
                , fileName = pth + item + suffix
            sht.Range('A1').Resize(data.length, data[0].length).Value2 = data
            
            //sht.Name = item
            let ifErr = trycatch(()=>{sht.Name = item}, ()=>{alert(shtErr)}) //错误处理回调函数
            if (ifErr) return
            
            //wb.SaveAs(fileName)
            ifErr = trycatch(()=>{wb.SaveAs(fileName)}, ()=>{alert(wbErr)})
            if (ifErr) return
            
            wb.Close(false)
        }
    
    }
    
    //执行完成后提示信息
    if (mode != '工作表') {
        let tip = '数据拆分完成,是否打开文件夹?\n文件路径在:' + pth
            , msg = MsgBox(tip, jsYesNo)
        if (msg == 6) return Shell('cmd /c start ' + pth, jsHide) //调用cmd命令打开文件夹
        
    } else {
        alert('完成')
    }
    
}

//拆分工作表数据_选项
function splitSheet_option() {
    let splitMode = GetSetting('addins', 'splitSheetTools', 'option')
    if (splitMode) {
        let mode = (splitMode == '工作表') ? '工作簿' : '工作表'
            , tip = '当前数据拆分模式为: ' + splitMode + '\n是否切换为' + mode + '?'
            , msg = MsgBox(tip, jsYesNo)
        if (msg == 6) SaveSetting('addins', 'splitSheetTools', 'option', mode)
        
        
    } else {
        let tip = '当前拆分模式为:工作表\n是否将数据拆分成工作簿?'
            , msg = MsgBox(tip, jsYesNo)
            
        if (msg == 6) return SaveSetting('addins', 'splitSheetTools', 'option', '工作簿')
        SaveSetting('addins', 'splitSheetTools', 'option', '工作表')
    }
}

//合并Excel文件
function mergeExcelFile() {
    let pth = getFilePath()
    if (!pth) return //用户未选择文件则退出执行
    
    let text = GetSetting('addins', 'excelMergeTools', 'option')
        , option = text ? JSON.parse(text) : {} //将配置信息转化js对象
        , data = option.OptionButton2 ? getFileData(pth, 1) : getFileData(pth) //判断文件合并模式
    if (!data) return //没有读取到数据则退出执行
    
    let wb = Workbooks.Add() //新建工作簿保存数据
        , sht = wb.Sheets(1)
        , sPath = ''
        
    sht.Range('A1').Resize(data.length, data[0].length).Value2 = data //写入数据
    
    if (option.CheckBox1) {
        sPath = Application.GetSaveAsFilename() //弹出文件保存对话框,手动指定文件路径和文件名
        
    } else { //读取用户指定的文件路径、文件名
        let filePath = option.TextEdit2 ? option.TextEdit2 : Application.DefaultFilePath + '\\'
            , fileName = option.TextEdit1 ? option.TextEdit1 + dateFormat('yyyymmddSS') : 'newfile' + dateFormat('yyyymmddHHMMSS')
            , suffix = pth[0].slice(pth[0].lastIndexOf('.'))
            
        sPath = filePath + fileName + suffix
    }
    
    wb.SaveAs(sPath) //保存工作簿
}

//合并Excel文件_选项
function mergeExcelFile_option() {
    UserForm1.Show()
}

三、可以多次复用的模块化函数

实际开发过程大多都是用现成代码完成的,平时写代码的时候可以尝试封装可以多次复用的代码块,等下次需要用到类似功能的时候,直接复制粘贴改改就能用。

//以下均为模块化函数,具体案例参考我往期写的专栏,这里不多赘述
//需要的时候直接复制就能用,实际开发过程中,大部分都是用现成代码修改完成的

//对话框获取文件完整路径
function getFilePath() {
    let fd = Application.FileDialog(msoFileDialogFilePicker), pth = []
    fd.Filters.Clear()
    fd.Filters.Add('Excel文件', '*.xls;*.xlsx;*.csv')
    fd.Title = '请选文件'
    if (fd.Show() != -1) {
        alert('你没选中文件!')
        return ''
    }
    for (let i = 0; i < fd.SelectedItems.Count; i++) {
        pth[i] = fd.SelectedItems.Item(i + 1)
    }
    return pth
}
 
//循环读取每个工作簿的工作表数据
function getFileData(pth, all) {
    let data = [], i = 0, th = ''
    for (let mypth of pth) {
        let wb = Workbooks.Open(mypth), noall = all ? 0 : wb.ActiveSheet.Index
        for (let sht of wb.Worksheets) {
            if (noall && sht.Index != noall) continue
            let arr = sht.Range('A1').CurrentRegion.Value2
            
            if (!arr) {
                let tip = '无法识别数据区域!\n是否继续执行并忽略该工作簿/表', yesno = MsgBox(tip, jsYesNo)
                if (yesno != 6) return
                continue
            }
            
            if (i == 0) th = arr[0].join()
            if (th != arr[0].join()) {
                let tip = '表头字段不一致!\n是否继续执行并忽略该工作簿/表', yesno = MsgBox(tip, jsYesNo)
                if (yesno != 6) return
                continue
            }
            
            if (i > 0) arr.shift()
            data = data.concat(arr)
            i++
        }
        wb.Close(false)
    }
    
    return data
}

//日期格式化
function dateFormat(fmt, date) {
    fmt = fmt || 'yyyy-mm-dd HH:MM:SS'
    date = date || new Date
    const opt = {
        'y+': date.getFullYear().toString()
        , 'm+': (date.getMonth() + 1).toString()
        , 'd+': date.getDate().toString()
        , 'H+': date.getHours().toString()
        , 'M+': date.getMinutes().toString()
        , 'S+': date.getSeconds().toString()
    }
    for (let k in opt) {
        let ret = new RegExp('(' + k + ')').exec(fmt)
        if (ret) {
            fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, '0')))
        }
    }
    return fmt
}

//错误处理
function trycatch(f, e) {
    try {
        f()
    } catch {
        e()
        return '1'
    }
}

xlam加载宏源码文件地址

https://download.csdn.net/download/huaermeier/87522018

你可能感兴趣的:(Excel,JS,javascript,excel,wps)