Excel VBA 事件入门,客户跟进表里最常用的几个自动触发怎么写
- 2026-11-06 08:32:25
客户跟进表每天都会被改:有人补回访日期,有人把状态从“待回访”改成“已成交”,有人临下班前再另存一个版本。表被改得越频繁,越适合把提醒、校验和收尾动作交给事件去做。
事件在 VBA 里面是一个比较抽象的概念,我们可以这样去理解:
每当表格有变化,在 VBA 里面都可以理解成一个“事件”。比如:
你点击了表格的某一个单元格 你修改了它的数据
这种操作都属于一个事件。
而 VBA 的“事件编程”,就是指在这些事件发生的时候,同时调用一段自己写的程序来运行。这样理解的话可能会好一点。
VBA 事件本身不难,难的是别把过程写错地方,也别让代码把自己再触发一遍。把工作簿事件、工作表事件,很多原本靠人记着做的动作,就能自己跑起来。
这里的学习,我觉得应该是先学常用的一些事件。
像我下面列出来的这些常用事件,我们可以先用起来。因为 VBA 里面的事件非常多,不要先求全。我们应该在实际工作的过程中,遇到了再去通过查资料的方式,找到我们需要的一个事件。
先分清谁在响应
Workbook_OpenWorkbook_BeforeSave、Workbook_NewSheet | ThisWorkbook | |
Worksheet_ChangeWorksheet_SelectionChange、Worksheet_BeforeDoubleClick | ||
WorkbookOpenSheetChange 这类应用程序事件 |

入门阶段先把前两类写明白就够用了。只要记住一句话:工作簿级事件写在 ThisWorkbook,工作表级事件写在具体那张表的代码窗口里,别丢到普通模块里假装能触发。这是一个非常重要的问题,也是很容易犯错的一个地方。
我们在写事件的时候,必须把位置写清楚、写对:
属于工作簿的,就写在工作簿(ThisWorkbook)里面 具体到某个工作表的,就写在对应的工作表里面
先看一张客户跟进表
假设 B3:G8 是销售团队每天都在改的跟进总表:
这类表最常见的自动触发,通常就两组:
打开文件时先把表定位到业务入口。 跟进状态或联系日期被改动后,自动补出回访日期和提醒内容。
打开和保存两个动作,放在 ThisWorkbook 里最合理
如果你想让工作簿一打开就跳到跟进总表,顺手提醒还有多少条待跟进记录,可以直接写在 ThisWorkbook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
PrivateSub Workbook_Open()With Worksheets("跟进总表").Activate.Range("B3").Select' 打开时先提示今天还有多少条待回访MsgBox "今天还有 " & WorksheetFunction.CountIf(.Range("D4:D8"), "待回访") & _" 条记录要继续跟。"EndWithEndSubPrivateSub Workbook_BeforeSave(ByVal SaveAsUI AsBoolean, Cancel AsBoolean)If SaveAsUI Then' 另存为前补一句提醒,避免版本乱放MsgBox "这份跟进簿建议另存到本周归档文件夹,后面追版本会省事很多。"EndIfEndSub
这里的 SaveAsUI 很实用。它为 True 时,说明这次保存会弹“另存为”窗口,常见于新文件第一次保存,或者当前文件是只读打开。很多版本管理提醒,都适合挂在这里。
真正高频的,还是 Worksheet_Change
真正每天都在跑的,多半还是工作表里的输入变化。上面这张表里,我只关心 D4:E8 这块输入区,也就是“当前状态”和“本次联系”两列。状态或日期一变,就顺手把 F 列和 G 列补出来。
这段代码应该放在“跟进总表”那张工作表自己的代码模块里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
PrivateSub Worksheet_Change(ByVal Target As Range)Dim watchRange As RangeDim editCells As RangeDim oneCell As RangeDim dataRow AsLongSet watchRange = Range("D4:E8")Set editCells = Intersect(Target, watchRange)If editCells IsNothingThenExitSubOnErrorGoTo CleanUpApplication.EnableEvents = FalseForEach oneCell In editCellsdataRow = oneCell.RowIfCells(dataRow,"D").Value="待回访"AndIsDate(Cells(dataRow,"E").Value) ThenCells(dataRow,"F").Value=Cells(dataRow,"E").Value+3' 三天后回访Cells(dataRow, "G").Value = "三天内回访"ElseIfCells(dataRow,"D").Value="已报价"AndIsDate(Cells(dataRow,"E").Value) ThenCells(dataRow,"F").Value=Cells(dataRow,"E").Value+2' 两天后追报价反馈Cells(dataRow, "G").Value = "两天内催报价反馈"ElseIf Cells(dataRow, "D").Value = "已成交"ThenCells(dataRow, "F").ClearContentsCells(dataRow, "G").Value = "转售后跟进"ElseCells(dataRow, "F").ClearContentsCells(dataRow, "G").ClearContentsEndIfNext oneCellCleanUp:Application.EnableEvents = TrueEndSub
这段写法里,最关键的是下面三个点:
Intersect(Target, watchRange)先把范围收窄,不让整张表任何一个改动都把事件带起来。Target可能不止一格,批量粘贴时也一样会进来,所以直接遍历editCells更稳。事件过程里又写了 F列和G列,如果不先Application.EnableEvents = False,代码就会继续触发自己。
有三个触发细节别想当然
Worksheet_Change不会因为公式重算而触发。公式结果变了,但用户没直接改单元格内容时,它不会进来。单纯改格式通常不触发 Change,但复制粘贴格式、清除格式这类动作又可能把它带起来,关键录入表别只按直觉判断。只要事件过程里又改了工作表,Excel 的撤销栈经常就保不住了。录入表真要上事件,最好先接受这一点,再决定哪些动作值得自动化。
客户跟进表这类每天都在改的工作簿,最有用的不是把事件种类全背下来,而是先抓住三个动作:打开时准备好、保存前拦一下、输入后只盯住真正的业务区。把这几类写对了,后面再补 SelectionChange、BeforeDoubleClick 这些细一点的触发,学起来会容易一些。