0
点赞
收藏
分享

微信扫一扫

JS宏综合示例-多维度筛选统计(带窗体)


📢作者: 小小明-代码实体

📢博客主页​

📢欢迎点赞 👍 收藏 ⭐留言 📝 欢迎讨论!

需求描述

客户每天都有一份类似如下结构的数据:

JS宏综合示例-多维度筛选统计(带窗体)_开发语言

但是实际数据远不止这些列,列的顺序也完全随机。

要求:

  • 将业务类型为包裹,体积为0的数据修改为体积=重量/167。
  • 筛选目的地为香港,业务类型为零担+包裹
  • 然后提供一个输入框筛选车牌
  • 对筛选后的数据生成透视表,统计每个业务员每个网点的总体积,以及每个客户每个网点的总体积。

数据和完整代码:​​https://gitcode.net/as604049322/blog_data​​

测试

列位置查找

虽然列位置不固定,但是客户能够保证列名是固定。那么我们可以实现自动查找对应列所在的位置。打开宏编辑器,测试如下代码:

function test()
{
Console.clear();
let max_col = Cells(1,255).End(xlToLeft).Column;
var [sale,client,c1,c2,c3,c4,c5,c6,c7] = [0,0,0,0,0,0,0,0,0,0];
for(let i=1;i<=max_col;i++) {
let cell=Cells.Item(1,i);
if(cell.Value2=="业务类型")
c1=i;
else if(cell.Value2=="重量(KG)")
c2=i;
else if(cell.Value2=="体积(m³)")
c3=i;
else if(cell.Value2=="车牌")
c4=i;
else if(cell.Value2=="目的地点")
c5=i;
else if(cell.Value2=="开单网点")
c6=i;
else if(cell.Value2=="始发地点")
c7=i;
else if(cell.Value2=="业务员")
sale=i;
else if(cell.Value2=="客户")
client=i;
if([sale,client,c1,c2,c3,c4,c5,c6,c7].every(v=>v>0))break;
}
Console.log(`业务员:${sale},客户:${client},业务类型:${c1},车牌:${c4}`)
Console.log(`重量:${c2},体积:${c3},开单网点:${c6},始发地点:${c7},目的地点:${c5}`);
}

运行代码,立即窗口中显示结果:

业务员:2,客户:3,业务类型:4,车牌:9
重量:7,体积:8,开单网点:10,始发地点:11,目的地点:12

可以看到已经顺利实现了自动查找对应列位置。

接收车牌输入

然后我们先考虑使用输入框简单实现:

function test2()
{
Console.clear();
let text=InputBox("请输入要筛选的车牌","请输入要筛选的车牌,多个车牌使用空格或+分隔");
var arr = text.split(/[\s+]+/);
console.log(arr.join(","));
}

运行后,随意测试一组车牌:

JS宏综合示例-多维度筛选统计(带窗体)_javascript_02

点击确定后,立即窗口打印结果如下:

FWRU0218853,CICU2847731,CICU2846870,CICU2189878

测试完成,后续可以通过该数组进行车牌过滤。

后续客户希望输入框能够提示数据表中存在的车牌列表进行提示,于是我们可以设计窗体来实现。

在宏编辑器中右键单击当前文件,点击插入->用户窗体,经过一翻拖拽,我设计的界面如下:

JS宏综合示例-多维度筛选统计(带窗体)_前端_03

这里只用到按钮、标签、文本框和复合框四种控件,上图中复合框的ID为ComboBox2,文本框的ID为TextEdit1。

事件响应代码为:

function UserForm1_ComboBox2_Change()
{
if(UserForm1.TextEdit1.Value!="")
UserForm1.TextEdit1.Value+="+";
UserForm1.TextEdit1.Value+=UserForm1.ComboBox2.Value;
}

function UserForm1_CommandButton1_Click()
{
UserForm1.TextEdit1.Value="";
}

function UserForm1_CommandButton2_Click()
{
UserForm1.Close()
}

然后我们测试接着前面的列位置查找后面测试如下代码:

var end_row=Range("A1").End(xlDown).Row;
var nums=Range(Cells.Item(2,c4),Cells.Item(end_row,c4)).Value().flat();
nums = Array.from(new Set(nums));
UserForm1.TextEdit1.Value=nums.filter(v=>/^[A-z]/.test(v)).join("+");
for(let num of nums){
UserForm1.ComboBox2.AddItem(num);
}
UserForm1.Label4.Caption=`业务员:${sale},客户:${client},业务类型:${c1},车牌:${c4}\n重量(KG):${c2},体积(m³):${c3},开单网点:${c6},始发地点:${c7},目的地点:${c5}`;
UserForm1.Show();
let text=UserForm1.TextEdit1.Value;
var arr = text.split(/[\s+]+/);
console.log(arr.join(","));

运行后:

JS宏综合示例-多维度筛选统计(带窗体)_前端_04

点击清空输入即可将文本框置空,点击复合框则当前点击项会添加到文本输入框中:

JS宏综合示例-多维度筛选统计(带窗体)_开发语言_05

然后我们关闭窗体或点击生成按钮,立即窗口打印出:

CICU2189878,CICU2847731

从开单网点抽取仓库关键字

前面我在《WPS增加正则处理函数,简直如虎添翼》一文中演示了如何在wps中使用正则函数。下面我们可以基于前文使用该公式测试:

var end_row=Range("A1").End(xlDown).Row;
Cells.Item(1, c3).Select();
// 取消或设置筛选状态
Selection.AutoFilter();
// 修改始发地点公式
Cells.Item(2, c7).FormulaR1C1=`=re_draw(RC[${c6-c7}],"(广州|福永|平湖)")`;
Cells.Item(2, c7).Select();
if(end_row>=2){
Selection.AutoFill(Range(Cells.Item(2,c7),Cells.Item(end_row,c7)), xlFillDefault);
}

基于前面已经找到的列位置,运行后结果如下:

JS宏综合示例-多维度筛选统计(带窗体)_正则_06

可以看到顺利的抽取出来对应的关键字。

若没有按照前文添加正则函数,可以直接在本文件中添加如下代码:

function readValue(target){
return typeof(target)=="string"?target:target.Item(1).Value2
}
//正则抽取
function re_draw(text,){
text=readValue(text),pattern=readValue(pattern);
let regexp = new RegExp(pattern,"g");
let arr=regexp.exec(text);
arr.shift();
return arr.map(v=>v||"");
}

其他测试

有关过滤和创建宏的代码可以借助录制宏生成的代码作为参考,例如:

/**
* Macro1 Macro
* 宏由 ASUS 录制,时间: 2022/10/01
*/
function Macro1()
{
Selection.AutoFilter(undefined, undefined, xlAnd, undefined, undefined);
Range("A1:L379").AutoFilter(4, Array("包裹", "零担"), xlFilterValues, undefined, undefined);
Range("A1:L379").AutoFilter(12, Array("香港"), xlFilterValues, undefined, undefined);
Range("A1:L379").AutoFilter(9, Array("APHU4352841", "CCLU9911592", "CICU2189878", "CICU2846870", "CICU2846904", "CICU2847731", "FWRU0218853"), xlFilterValues, undefined, undefined);
ActiveWorkbook.PivotCaches.Create(xlDatabase, "=sheet!R1C1:R380C12", xlPivotTableVersion15);
Sheets.Add(undefined, undefined, undefined, undefined);
ActiveWorkbook.PivotCache.CreatePivotTable("Sheet2!R3C1", "数据透视表1", undefined, xlPivotTableVersion15);
Range("A3").Activate();
(obj=>{
obj.Orientation = xlRowField;
obj.Position = 1;
})(ActiveSheet.PivotTables("数据透视表1").PivotFields("业务员"));
(obj=>{
obj.Orientation = xlColumnField;
obj.Position = 1;
})(ActiveSheet.PivotTables("数据透视表1").PivotFields("开单网点"));
ActiveSheet.PivotTables("数据透视表1").AddDataField(ActiveSheet.PivotTables("数据透视表1").PivotFields("体积(m³)"), undefined, undefined);
ActiveWindow.ScrollRow = 1;

}

最终代码

基于以上测试,我最终编写的完整代码为:

function readValue(target){
return typeof(target)=="string"?target:target.Item(1).Value2
}
//正则抽取
function re_draw(text,){
text=readValue(text),pattern=readValue(pattern);
let regexp = new RegExp(pattern,"g");
let arr=regexp.exec(text);
arr.shift();
return arr.map(v=>v||"");
}

function 透视表生成_窗体版()
{
Console.clear();
let max_col = Cells(1,255).End(xlToLeft).Column;
var [sale,client,c1,c2,c3,c4,c5,c6,c7] = [0,0,0,0,0,0,0,0,0,0];
for(let i=1;i<=max_col;i++) {
let cell=Cells.Item(1,i);
if(cell.Value2=="业务类型")
c1=i;
else if(cell.Value2=="重量(KG)")
c2=i;
else if(cell.Value2=="体积(m³)")
c3=i;
else if(cell.Value2=="车牌")
c4=i;
else if(cell.Value2=="目的地点")
c5=i;
else if(cell.Value2=="开单网点")
c6=i;
else if(cell.Value2=="始发地点")
c7=i;
else if(cell.Value2=="业务员")
sale=i;
else if(cell.Value2=="客户")
client=i;
if([sale,client,c1,c2,c3,c4,c5,c6,c7].every(v=>v>0))break;
}
Console.log(`业务员:${sale},客户:${client},业务类型:${c1},车牌:${c4}`)
Console.log(`重量:${c2},体积:${c3},开单网点:${c6},始发地点:${c7},目的地点:${c5}`);
if(![sale,client,c1,c2,c3,c4,c5,c6,c7].every(v=>v>0)){
let tip = `
<p>业务员:${sale},客户:${client},业务类型:${c1},车牌:${c4}</p>
<p>重量(KG):${c2},体积(m³):${c3},开单网点:${c6},始发地点:${c7},目的地点:${c5}</p>
<p style="color:red">列号为0的列表示未找到,请检查对应列是否存在!</p>
<p style="color:green;text-shadow: 1px 1px 2px red, 0 0 1em blue, 0 0 0.2em blue;">
${new Date().toLocaleString()}
</p>
`;
alert(tip);
return;
};
var end_row=Range("A1").End(xlDown).Row;
if(end_row==1) {
alert("数据表无数据!结束任务!")
return;
};
var nums=Range(Cells.Item(2,c4),Cells.Item(end_row,c4)).Value().flat();
nums = Array.from(new Set(nums));
UserForm1.TextEdit1.Value=nums.filter(v=>/^[A-z]/.test(v)).join("+");
for(let num of nums)
UserForm1.ComboBox2.AddItem(num);
UserForm1.Label4.Caption=`业务员:${sale},客户:${client},业务类型:${c1},车牌:${c4}\n重量(KG):${c2},体积(m³):${c3},开单网点:${c6},始发地点:${c7},目的地点:${c5}`;
UserForm1.Show();
let text=UserForm1.TextEdit1.Value;
Console.log(text);
if(text=="") {
alert("请输入要筛选的车牌后继续");
return;
}
Cells.Item(1, c3).Select();
// 取消或设置筛选状态
Selection.AutoFilter();

// 修改始发地点公式
Cells.Item(2, c7).FormulaR1C1=`=re_draw(RC[${c6-c7}],"(广州|福永|平湖)")`;
Cells.Item(2, c7).Select();
if(end_row>=2)
Selection.AutoFill(Range(Cells.Item(2,c7),Cells.Item(end_row,c7)), xlFillDefault);

// 读取表格数据区域
let rng = Range(Range("A1"),Cells.Item(end_row, max_col));
Console.log(rng.Address());
rng.AutoFilter(c1, Array("包裹"), xlFilterValues);
rng.AutoFilter(c3, Array("0",""), xlFilterValues);
if(Range("A65536").End(xlUp).Row>1) {
// 避免筛选结果为空的情况
let start_row = Rows("2:"+end_row).SpecialCells(xlCellTypeVisible).Row
Cells.Item(start_row, c3).FormulaR1C1=`=RC[${c2-c3}]/167`;
Cells.Item(start_row,c3).Select();
Selection.AutoFill(Range(Cells.Item(start_row,c3),Cells.Item(end_row,c3)), xlFillDefault);
}
// 删除之前产生的结果表
Application.DisplayAlerts=false;
for(let sht of Sheets) {
if(sht.Name=="筛选" || sht.Name=="透视表"){
sht.Delete();
}
}
Application.DisplayAlerts=true;
Cells.Item(1, c3).Select();
// 取消或设置筛选状态
Selection.AutoFilter();
rng.AutoFilter(c1, Array("包裹", "零担"), xlFilterValues);
rng.AutoFilter(c5, Array("香港"), xlFilterValues);
var arr = text.split(/[\s+]+/);
let [a,b]=arr.slice(0,2);
rng.AutoFilter(c4, arr, xlFilterValues);
if(Range("A65536").End(xlUp).Row==1){
alert("筛选结果为空,请检查输入的车牌是否存在!");
return
}
// 仅复制所需要的列
let areas=[sale,client,c1,c2,c3,c4,c5,c7].map(c=>Range(Cells.Item(1,c),Cells.Item(end_row,c)));
areas = Union(...areas);
areas.Copy();

var sht=Worksheets.Add();
sht.Name="筛选";
sht.Range("A1").Select();
sht.Paste();
sht.Cells.EntireColumn.AutoFit()
end_row = sht.Range("A65536").End(xlUp).Row;
max_col = sht.Range("A1").End(xlToRight).Column;
let model = ActiveWorkbook.PivotCaches().Create(xlDatabase, `=筛选!R1C1:R${end_row}C${max_col}`, xlPivotTableVersion15);

sht = Worksheets.Add();
sht.Name="透视表";
model.CreatePivotTable("透视表!R3C1", "数据透视表1", undefined, xlPivotTableVersion15);
(obj=>{
obj.PivotFields("业务员").Orientation = xlRowField;
obj.PivotFields("始发地点").Orientation = xlColumnField;
obj.AddDataField(obj.PivotFields("体积(m³)"));
})(sht.PivotTables("数据透视表1"));

model.CreatePivotTable("透视表!R25C1", "数据透视表2", undefined, xlPivotTableVersion15);
(obj=>{
obj.PivotFields("客户").Orientation = xlRowField;
obj.PivotFields("始发地点").Orientation = xlColumnField;
obj.AddDataField(obj.PivotFields("体积(m³)"));
})(sht.PivotTables("数据透视表2"));
}

运行代码,窗体基于默认输入直接点击生成按钮,得到的筛选结果:

JS宏综合示例-多维度筛选统计(带窗体)_开发语言_07

生成的透视表为:

JS宏综合示例-多维度筛选统计(带窗体)_前端_08


举报

相关推荐

0 条评论