WPS Excel 初次宏使用
进入WPS宏编辑器 :WPS Excel-开发工具-切换到JS环境-查看代码
在宏编辑器里添加已有的JS代码:文件-导入文件-选中所需JS文件
以此代码为例,来感受并记录一下整个流程
实现的功能是:将"汇总表"中标绿学生信息,按照学号到"未就业"表中进行匹配,将匹配的行删除。
尚未研究明白如何进行跨模块调用
javascript
// 主函数:汇总表含目标绿色单元格的行→提取学号→未就业表匹配标黄→询问删除(批量/逐个)
function highlightAndDeleteMatchedRows() {
try {
// 1. 基础配置(核心:指定目标绿色RGB值)
var config = {
summaryHeaderRow: 1, // 汇总表表头行(默认第1行)
unemployedHeaderRow: 4, // 未就业表表头行(默认第1行)
targetHeader: "学号", // 目标表头名称(可修改为实际名称)
targetGreenRGB: { r: 146, g: 208, b: 80 }, // 目标绿色精确RGB值
colorTolerance: 5, // 颜色容差(0-10,兼容轻微偏差)
checkColumnCount: 50 // 每行检查前50列(可调整)
};
// 存储未就业表中匹配并标黄的行信息(行号+学号)
var highlightedRows = [];
// 2. 获取当前工作簿
var currentWorkbook = Application.ActiveWorkbook;
if (!currentWorkbook) {
console.log("❌ 未检测到打开的工作簿,请先打开目标表格!");
return;
}
console.log("✅ 成功获取当前工作簿:", currentWorkbook.Name);
// 3. 获取目标工作表
var summarySheet = currentWorkbook.Sheets("汇总表");
var unemployedSheet = currentWorkbook.Sheets("未就业");
if (!summarySheet) {
console.log("❌ 未找到名为「汇总表」的工作表,请检查名称!");
return;
}
if (!unemployedSheet) {
console.log("❌ 未找到名为「未就业」的工作表,请检查名称!");
return;
}
console.log("✅ 成功加载工作表:汇总表、未就业表");
// 4. 封装函数:查找表头对应的列号 + 输出表头行数据
function findHeaderColumn(sheet, headerRow, targetHeader, sheetName) {
try {
var headerRowData = "📋 " + sheetName + "第" + headerRow + "行(表头行)实际数据:\n";
var hasData = false;
for (var col = 1; col <= 10; col++) {
var cellValue = sheet.Cells(headerRow, col).Value();
var cellText = cellValue ? cellValue.toString().trim() : "【空值】";
headerRowData += "第" + col + "列:" + cellText + "\n";
if (cellText !== "【空值】") hasData = true;
}
console.log(headerRowData);
if (!hasData) return -2;
// 查找目标表头(兼容空格)
for (var col = 1; col <= 100; col++) {
var cellValue = sheet.Cells(headerRow, col).Value();
var cellText = cellValue ? cellValue.toString().replace(/\s+/g, "") : "";
var targetText = targetHeader.replace(/\s+/g, "");
if (cellText === targetText) return col;
}
return -1;
} catch (e) {
console.log("❌ " + sheetName + "表头行查找出错:", e.message);
return -3;
}
}
// 5. 封装函数:判断单元格是否为目标绿色
function isTargetGreenCell(cell) {
try {
var interiorColor = cell.Interior.Color;
var red = interiorColor & 0xFF;
var green = (interiorColor >> 8) & 0xFF;
var blue = (interiorColor >> 16) & 0xFF;
var isMatch =
Math.abs(red - config.targetGreenRGB.r) <= config.colorTolerance &&
Math.abs(green - config.targetGreenRGB.g) <= config.colorTolerance &&
Math.abs(blue - config.targetGreenRGB.b) <= config.colorTolerance;
return { isMatch: isMatch, r: red, g: green, b: blue };
} catch (e) {
return { isMatch: false, r: 0, g: 0, b: 0 };
}
}
// 6. 查找汇总表的「学号」列
var summaryStudentIdCol = findHeaderColumn(summarySheet, config.summaryHeaderRow, config.targetHeader, "汇总表");
switch (summaryStudentIdCol) {
case -3: console.log("❌ 汇总表表头查找过程出错,请检查表格是否损坏!"); return;
case -2: console.log("❌ 汇总表第" + config.summaryHeaderRow + "行(表头行)无有效数据!"); return;
case -1: console.log("❌ 在汇总表第" + config.summaryHeaderRow + "行未找到「" + config.targetHeader + "」表头!"); return;
default: console.log("✅ 汇总表「" + config.targetHeader + "」列位置:第" + summaryStudentIdCol + "列");
}
// 7. 核心步骤1:遍历汇总表,收集含目标绿色单元格的行的学号
var summaryUsedRange = summarySheet.UsedRange;
if (!summaryUsedRange) {
console.log("❌ 汇总表无有效数据!");
return;
}
var summaryLastRow = summaryUsedRange.Rows.Count;
if (summaryLastRow <= config.summaryHeaderRow) {
console.log("❌ 汇总表除表头外无其他数据!");
return;
}
console.log(`ℹ️ 汇总表数据总行数:${summaryLastRow - config.summaryHeaderRow}行,开始查找含目标绿色单元格的行...`);
var greenRowStudentIds = [];
var foundGreenRows = new Set();
for (var row = config.summaryHeaderRow + 1; row <= summaryLastRow; row++) {
var hasTargetGreen = false;
var greenCellInfo = "";
for (var col = 1; col <= config.checkColumnCount; col++) {
var currentCell = summarySheet.Cells(row, col);
var { isMatch, r, g, b } = isTargetGreenCell(currentCell);
if (isMatch) {
hasTargetGreen = true;
greenCellInfo += `第${col}列(RGB(${r},${g},${b}))、`;
}
}
if (hasTargetGreen) {
greenCellInfo = greenCellInfo.slice(0, -1);
console.log(`📌 汇总表第${row}行 → 含目标绿色单元格:${greenCellInfo}`);
if (!foundGreenRows.has(row)) {
var studentId = summarySheet.Cells(row, summaryStudentIdCol).Value();
studentId = studentId ? studentId.toString().trim() : "";
if (studentId) {
greenRowStudentIds.push(studentId);
foundGreenRows.add(row);
console.log(` → 该行学号:${studentId}`);
} else {
console.log(`⚠️ 汇总表第${row}行(含目标绿色)但学号列无有效数据,跳过!`);
}
}
}
}
if (greenRowStudentIds.length === 0) {
console.log("❌ 汇总表中未找到任何含目标绿色(RGB(146,208,80))单元格的行!");
return;
}
console.log(`✅ 共找到${foundGreenRows.size}行含目标绿色单元格,收集到${greenRowStudentIds.length}个有效学号:`, greenRowStudentIds);
// 8. 查找未就业表的「学号」列
var unemployedStudentIdCol = findHeaderColumn(unemployedSheet, config.unemployedHeaderRow, config.targetHeader, "未就业表");
switch (unemployedStudentIdCol) {
case -3: console.log("❌ 未就业表表头查找过程出错,请检查表格是否损坏!"); return;
case -2: console.log("❌ 未就业表第" + config.unemployedHeaderRow + "行(表头行)无有效数据!"); return;
case -1: console.log("❌ 在未就业表第" + config.unemployedHeaderRow + "行未找到「" + config.targetHeader + "」表头!"); return;
default: console.log("✅ 未就业表「" + config.targetHeader + "」列位置:第" + unemployedStudentIdCol + "列");
}
// 9. 核心步骤2:在未就业表中匹配学号,标黄对应行并记录行信息
var unemployedUsedRange = unemployedSheet.UsedRange;
if (!unemployedUsedRange) {
console.log("❌ 未就业表无有效数据!");
return;
}
var unemployedLastRow = unemployedUsedRange.Rows.Count;
if (unemployedLastRow <= config.unemployedHeaderRow) {
console.log("❌ 未就业表除表头外无其他数据!");
return;
}
console.log(`ℹ️ 未就业表数据总行数:${unemployedLastRow - config.unemployedHeaderRow}行,开始批量匹配...`);
// 遍历未就业表,标黄匹配行并记录
for (var i = config.unemployedHeaderRow + 1; i <= unemployedLastRow; i++) {
var currentId = unemployedSheet.Cells(i, unemployedStudentIdCol).Value();
currentId = currentId ? currentId.toString().trim() : "";
if (greenRowStudentIds.includes(currentId)) {
var matchedRange = unemployedSheet.Rows(i);
matchedRange.Interior.Color = RGB(255, 255, 0); // 标黄
highlightedRows.push({ rowNum: i, studentId: currentId }); // 记录行号和学号
console.log(`✅ 未就业表第${i}行(学号:${currentId})已标黄!`);
}
}
// 无标黄行时直接结束
if (highlightedRows.length === 0) {
console.log("❌ 未就业表中无匹配的学号,未标黄任何行!");
MsgBox("未就业表中无匹配的学号,未标黄任何行!");
return;
}
// 10. 核心新增:弹窗询问删除方式
console.log(`ℹ️ 共标黄${highlightedRows.length}行,开始询问删除方式...`);
var deletePrompt = `已在未就业表中标黄${highlightedRows.length}行匹配数据!\n请选择删除方式:\n1 - 批量删除所有标黄行\n2 - 逐个确认删除标黄行\n3 - 取消删除`;
var userChoice = MsgBox(deletePrompt, 1 + 32 + 256); // 1=OK/Cancel + 32=问号图标 + 256=默认按钮1
// 处理用户选择(WPS MsgBox返回值:1=确定,2=取消,3=中止,4=重试,5=忽略,6=是,7=否)
// 这里通过输入框补充选择(因为MsgBox无法直接获取1/2/3输入,改用InputBox)
var userInput = InputBox(deletePrompt + "\n请输入数字1/2/3并点击确定", "选择删除方式", "1");
userInput = userInput ? userInput.trim() : "3"; // 默认为取消删除
switch (userInput) {
case "1":
// 批量删除所有标黄行(注意:删除行时需从下往上删,避免行号偏移)
console.log("📥 用户选择:批量删除所有标黄行");
// 按行号降序排序(从下往上删)
var sortedRows = highlightedRows.sort((a, b) => b.rowNum - a.rowNum);
var deletedCount = 0;
for (var idx = 0; idx < sortedRows.length; idx++) {
var rowInfo = sortedRows[idx];
try {
unemployedSheet.Rows(rowInfo.rowNum).Delete();
console.log(`✅ 批量删除:未就业表第${rowInfo.rowNum}行(学号:${rowInfo.studentId})`);
deletedCount++;
} catch (e) {
console.error(`❌ 批量删除失败:第${rowInfo.rowNum}行`, e.message);
}
}
MsgBox(`批量删除完成!\n共删除${deletedCount}行标黄数据(总标黄${highlightedRows.length}行)`);
console.log(`🎉 批量删除完成!删除${deletedCount}行,剩余${highlightedRows.length - deletedCount}行未删除`);
break;
case "2":
// 逐个确认删除
console.log("📥 用户选择:逐个确认删除标黄行");
var deletedCount = 0;
var skippedCount = 0;
// 按行号升序排序(从上往下确认)
var sortedRows = highlightedRows.sort((a, b) => a.rowNum - b.rowNum);
// 重新获取行号(避免之前删除导致的行号偏移,逐个处理时实时判断)
for (var idx = 0; idx < sortedRows.length; idx++) {
var rowInfo = sortedRows[idx];
// 重新验证行是否存在(避免已被删除)
try {
var rowExists = unemployedSheet.Rows(rowInfo.rowNum).Interior.Color === RGB(255, 255, 0);
if (!rowExists) {
console.log(`⚠️ 第${rowInfo.rowNum}行已不存在,跳过`);
skippedCount++;
continue;
}
var confirmDelete = MsgBox(`是否删除未就业表第${rowInfo.rowNum}行?\n学号:${rowInfo.studentId}`, 4 + 32); // 4=是/否 + 32=问号图标
if (confirmDelete === 6) { // 6=是
unemployedSheet.Rows(rowInfo.rowNum).Delete();
console.log(`✅ 逐个删除:未就业表第${rowInfo.rowNum}行(学号:${rowInfo.studentId})`);
deletedCount++;
} else { // 7=否
console.log(`⚠️ 用户取消删除:第${rowInfo.rowNum}行(学号:${rowInfo.studentId})`);
skippedCount++;
}
} catch (e) {
console.error(`❌ 逐个删除失败:第${rowInfo.rowNum}行`, e.message);
skippedCount++;
}
}
MsgBox(`逐个删除完成!\n共删除${deletedCount}行,跳过${skippedCount}行`);
console.log(`🎉 逐个删除完成!删除${deletedCount}行,跳过${skippedCount}行`);
break;
case "3":
// 取消删除
console.log("📥 用户选择:取消删除");
MsgBox("已取消删除操作,标黄行保留不变!");
break;
default:
// 输入无效,默认取消
console.log(`⚠️ 用户输入无效(${userInput}),默认取消删除`);
MsgBox("输入无效!请输入1/2/3,已取消删除操作");
break;
}
// 11. 输出最终统计结果
console.log("🎉 整体操作完成!");
console.log(`- 汇总表含目标绿色单元格行数:${foundGreenRows.size}行`);
console.log(`- 未就业表标黄行数:${highlightedRows.length}行`);
console.log(`- 用户选择删除方式:${userInput === "1" ? "批量删除" : userInput === "2" ? "逐个删除" : "取消删除"}`);
} catch (e) {
console.error("❌ 执行失败!", "具体错误:", e.message, "错误类型:", e.name);
MsgBox("执行失败!具体错误:" + e.message);
}
}
// 执行函数
//highlightAndDeleteMatchedRows();
只要在同一个Project(工作簿)里每个Module里的函数就能互相调用
(面相对象编程)
