🎯 本章目标:综合运用前九章所学知识,从零开始开发一个完整的自动化报表系统,掌握实际项目开发流程和最佳实践。
一、项目概述
1.1 项目背景
某公司需要每月生成销售报表,包含:
- 从多个数据源汇总数据
- 计算关键指标(销售额、利润、增长率等)
- 生成图表和分析报告
- 发送邮件给相关人员
手动处理耗时且容易出错,需要开发自动化系统。
1.2 系统功能需求
┌─────────────────────────────────────────────────────────┐
│ 自动化报表系统功能模块 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 数据导入 │ │ 数据处理 │ │ 报表生成 │ │
│ │ │ │ │ │ │ │
│ │ - CSV导入 │ │ - 数据清洗 │ │ - 数据表 │ │
│ │ - Excel导入 │ │ - 数据计算 │ │ - 图表 │ │
│ │ - 手动录入 │ │ - 数据验证 │ │ - 透视表 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 数据存储 │ │ 报表分发 │ │ 日志管理 │ │
│ │ │ │ │ │ │ │
│ │ - 历史数据 │ │ - 邮件发送 │ │ - 操作日志 │ │
│ │ - 配置信息 │ │ - 导出PDF │ │ - 错误日志 │ │
│ │ - 模板管理 │ │ - 打印 │ │ - 性能监控 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
1.3 系统架构设计
┌─────────────────────────────────────────────────────────┐
│ 系统架构设计 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 用户界面层 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ │
│ │ │ 主菜单 │ │ 配置界面│ │ 数据录入窗体 │ │ │
│ │ └─────────┘ └─────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 业务逻辑层 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ │
│ │ │数据处理 │ │报表生成 │ │邮件发送 │ │ │
│ │ └─────────┘ └─────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 数据存储层 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ │
│ │ │原始数据 │ │配置数据 │ │报表模板 │ │ │
│ │ └─────────┘ └─────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
二、项目结构设计
2.1 工作簿结构
自动化报表系统.xlsm
│
├── 【控制面板】 - 主界面,功能入口
├── 【原始数据】 - 导入的原始数据
├── 【处理数据】 - 清洗后的数据
├── 【报表】 - 生成的报表
├── 【图表】 - 数据可视化
├── 【配置】 - 系统配置参数
├── 【日志】 - 操作和错误日志
└── 【模板】 - 报表模板
2.2 VBA 模块结构
VBAProject
│
├── 模块
│ ├── modMain - 主程序入口
│ ├── modDataImport - 数据导入模块
│ ├── modDataProcess - 数据处理模块
│ ├── modReport - 报表生成模块
│ ├── modEmail - 邮件发送模块
│ ├── modUtils - 通用工具函数
│ └── modConfig - 配置管理模块
│
├── 类模块
│ ├── clsDataValidator - 数据验证类
│ ├── clsReport - 报表类
│ └── clsErrorHandler - 错误处理类
│
├── 用户窗体
│ ├── frmMain - 主菜单窗体
│ ├── frmDataImport - 数据导入窗体
│ ├── frmConfig - 配置管理窗体
│ └── frmProgress - 进度显示窗体
│
└── ThisWorkbook - 工作簿事件
三、核心代码实现
3.1 主程序入口(modMain)
Option Explicit
'============================================
' 模块名称:modMain
' 功能描述:主程序入口
'============================================
' 主入口
Public Sub Main()
On Error GoTo ErrorHandler
' 显示主菜单
frmMain.Show
Exit Sub
ErrorHandler:
MsgBox "系统启动失败!" & vbCrLf & _
"错误: " & Err.Description, vbCritical
End Sub
' 一键生成报表
Public Sub GenerateReport()
On Error GoTo ErrorHandler
' 初始化
InitializeSystem
' 显示进度窗体
frmProgress.Show False
frmProgress.SetProgress 0, "正在初始化..."
' 步骤 1:导入数据
frmProgress.SetProgress 10, "正在导入数据..."
ImportData
' 步骤 2:处理数据
frmProgress.SetProgress 30, "正在处理数据..."
ProcessData
' 步骤 3:生成报表
frmProgress.SetProgress 60, "正在生成报表..."
CreateReport
' 步骤 4:生成图表
frmProgress.SetProgress 80, "正在生成图表..."
CreateCharts
' 步骤 5:发送邮件(可选)
If GetConfigValue("AutoSendEmail") = "是" Then
frmProgress.SetProgress 90, "正在发送邮件..."
SendReportEmail
End If
' 完成
frmProgress.SetProgress 100, "完成!"
' 记录日志
WriteLog "报表生成完成"
' 关闭进度窗体
Unload frmProgress
MsgBox "报表生成完成!", vbInformation
CleanExit:
CleanupSystem
Exit Sub
ErrorHandler:
WriteLog "错误: " & Err.Number & " - " & Err.Description
Unload frmProgress
MsgBox "报表生成失败!" & vbCrLf & _
"错误: " & Err.Description, vbCritical
Resume CleanExit
End Sub
' 初始化系统
Private Sub InitializeSystem()
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
.DisplayAlerts = False
End With
End Sub
' 清理系统
Private Sub CleanupSystem()
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.EnableEvents = True
.DisplayAlerts = True
End With
End Sub
3.2 数据导入模块(modDataImport)
Option Explicit
'============================================
' 模块名称:modDataImport
' 功能描述:数据导入功能
'============================================
' 导入数据主函数
Public Sub ImportData()
On Error GoTo ErrorHandler
' 清空原始数据表
ClearSheet "原始数据"
' 根据配置选择数据源
Dim dataSource As String
dataSource = GetConfigValue("DataSource")
Select Case dataSource
Case "CSV文件"
ImportFromCSV
Case "Excel文件"
ImportFromExcel
Case "数据库"
ImportFromDatabase
Case Else
ImportFromCSV ' 默认 CSV
End Select
WriteLog "数据导入完成,共 " & GetRowCount("原始数据") & " 行"
Exit Sub
ErrorHandler:
Err.Raise Err.Number, "ImportData", Err.Description
End Sub
' 从 CSV 导入
Private Sub ImportFromCSV()
On Error GoTo ErrorHandler
Dim filePath As String
filePath = SelectCSVFile()
If filePath = "" Then Exit Sub
Dim fso As Object
Dim ts As Object
Dim lineContent As String
Dim fields() As String
Dim ws As Worksheet
Dim rowNum As Long
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.OpenTextFile(filePath, 1) ' 1 = ForReading
Set ws = ThisWorkbook.Worksheets("原始数据")
rowNum = 1
' 读取表头
If Not ts.AtEndOfStream Then
lineContent = ts.ReadLine
fields = Split(lineContent, ",")
Dim i As Integer
For i = 0 To UBound(fields)
ws.Cells(1, i + 1).Value = fields(i)
Next i
End If
' 读取数据
Do While Not ts.AtEndOfStream
rowNum = rowNum + 1
lineContent = ts.ReadLine
fields = Split(lineContent, ",")
For i = 0 To UBound(fields)
ws.Cells(rowNum, i + 1).Value = fields(i)
Next i
Loop
ts.Close
' 格式化表头
ws.Range("A1").EntireRow.Font.Bold = True
ws.Range("A1").EntireRow.Interior.Color = RGB(200, 200, 255)
Exit Sub
ErrorHandler:
Err.Raise Err.Number, "ImportFromCSV", "CSV导入失败: " & Err.Description
End Sub
' 选择 CSV 文件
Private Function SelectCSVFile() As String
Dim filePath As Variant
filePath = Application.GetOpenFilename( _
FileFilter:="CSV文件 (*.csv),*.csv", _
Title:="选择数据文件")
If filePath <> False Then
SelectCSVFile = CStr(filePath)
End If
End Function
' 从 Excel 导入
Private Sub ImportFromExcel()
On Error GoTo ErrorHandler
Dim filePath As String
filePath = SelectExcelFile()
If filePath = "" Then Exit Sub
Dim sourceWB As Workbook
Dim sourceWS As Worksheet
Dim targetWS As Worksheet
Dim lastRow As Long, lastCol As Long
Set sourceWB = Workbooks.Open(filePath, ReadOnly:=True)
Set sourceWS = sourceWB.Sheets(1)
Set targetWS = ThisWorkbook.Worksheets("原始数据")
' 获取数据范围
lastRow = sourceWS.Cells(sourceWS.Rows.Count, 1).End(xlUp).Row
lastCol = sourceWS.Cells(1, sourceWS.Columns.Count).End(xlToLeft).Column
' 复制数据
sourceWS.Range(sourceWS.Cells(1, 1), sourceWS.Cells(lastRow, lastCol)).Copy _
targetWS.Range("A1")
sourceWB.Close False
Exit Sub
ErrorHandler:
If Not sourceWB Is Nothing Then
sourceWB.Close False
End If
Err.Raise Err.Number, "ImportFromExcel", "Excel导入失败: " & Err.Description
End Sub
' 选择 Excel 文件
Private Function SelectExcelFile() As String
Dim filePath As Variant
filePath = Application.GetOpenFilename( _
FileFilter:="Excel文件 (*.xlsx;*.xlsm),*.xlsx;*.xlsm", _
Title:="选择数据文件")
If filePath <> False Then
SelectExcelFile = CStr(filePath)
End If
End Function
3.3 数据处理模块(modDataProcess)
Option Explicit
'============================================
' 模块名称:modDataProcess
' 功能描述:数据处理功能
'============================================
' 处理数据主函数
Public Sub ProcessData()
On Error GoTo ErrorHandler
' 清空处理数据表
ClearSheet "处理数据"
' 获取原始数据
Dim sourceWS As Worksheet
Dim targetWS As Worksheet
Set sourceWS = ThisWorkbook.Worksheets("原始数据")
Set targetWS = ThisWorkbook.Worksheets("处理数据")
' 复制表头
Dim lastCol As Long
lastCol = sourceWS.Cells(1, sourceWS.Columns.Count).End(xlToLeft).Column
sourceWS.Range(sourceWS.Cells(1, 1), sourceWS.Cells(1, lastCol)).Copy _
targetWS.Range("A1")
' 添加计算列
targetWS.Cells(1, lastCol + 1).Value = "月份"
targetWS.Cells(1, lastCol + 2).Value = "销售额(万)"
targetWS.Cells(1, lastCol + 3).Value = "利润率"
targetWS.Cells(1, lastCol + 4).Value = "增长率"
targetWS.Cells(1, lastCol + 5).Value = "评级"
' 处理每行数据
Dim lastRow As Long
Dim i As Long
lastRow = sourceWS.Cells(sourceWS.Rows.Count, 1).End(xlUp).Row
For i = 2 To lastRow
ProcessSingleRow sourceWS, targetWS, i, lastCol
Next i
' 格式化
FormatProcessedData targetWS
WriteLog "数据处理完成,共 " & lastRow - 1 & " 条记录"
Exit Sub
ErrorHandler:
Err.Raise Err.Number, "ProcessData", Err.Description
End Sub
' 处理单行数据
Private Sub ProcessSingleRow(sourceWS As Worksheet, targetWS As Worksheet, _
rowNum As Long, baseCol As Long)
Dim col As Long
' 复制原始数据
For col = 1 To baseCol
targetWS.Cells(rowNum, col).Value = sourceWS.Cells(rowNum, col).Value
Next col
' 计算:月份
Dim dateValue As Date
If IsDate(sourceWS.Cells(rowNum, 2).Value) Then
dateValue = CDate(sourceWS.Cells(rowNum, 2).Value)
targetWS.Cells(rowNum, baseCol + 1).Value = Format(dateValue, "yyyy-mm")
End If
' 计算:销售额(万)
Dim sales As Double
If IsNumeric(sourceWS.Cells(rowNum, 3).Value) Then
sales = CDbl(sourceWS.Cells(rowNum, 3).Value)
targetWS.Cells(rowNum, baseCol + 2).Value = sales / 10000
End If
' 计算:利润率
Dim profit As Double
If IsNumeric(sourceWS.Cells(rowNum, 4).Value) And sales > 0 Then
profit = CDbl(sourceWS.Cells(rowNum, 4).Value)
targetWS.Cells(rowNum, baseCol + 3).Value = profit / sales
End If
' 计算:增长率(与上月比较)
Dim growthRate As Double
If rowNum > 2 Then
Dim lastMonthSales As Double
If IsNumeric(sourceWS.Cells(rowNum - 1, 3).Value) Then
lastMonthSales = CDbl(sourceWS.Cells(rowNum - 1, 3).Value)
If lastMonthSales > 0 Then
growthRate = (sales - lastMonthSales) / lastMonthSales
targetWS.Cells(rowNum, baseCol + 4).Value = growthRate
End If
End If
End If
' 计算:评级
Dim rating As String
rating = CalculateRating(sales, profit / IIf(sales > 0, sales, 1), growthRate)
targetWS.Cells(rowNum, baseCol + 5).Value = rating
End Sub
' 计算评级
Private Function CalculateRating(sales As Double, profitRate As Double, _
growthRate As Double) As String
Dim score As Integer
' 销售额评分
If sales >= 100000 Then score = score + 40
If sales >= 50000 Then score = score + 30
If sales >= 10000 Then score = score + 20
' 利润率评分
If profitRate >= 0.3 Then score = score + 30
If profitRate >= 0.2 Then score = score + 20
If profitRate >= 0.1 Then score = score + 10
' 增长率评分
If growthRate >= 0.5 Then score = score + 30
If growthRate >= 0.2 Then score = score + 20
If growthRate >= 0 Then score = score + 10
' 评级
Select Case score
Case Is >= 80: CalculateRating = "A"
Case Is >= 60: CalculateRating = "B"
Case Is >= 40: CalculateRating = "C"
Case Else: CalculateRating = "D"
End Select
End Function
' 格式化处理后的数据
Private Sub FormatProcessedData(ws As Worksheet)
Dim lastRow As Long
Dim lastCol As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
lastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
With ws
' 表头格式
.Range(.Cells(1, 1), .Cells(1, lastCol)).Font.Bold = True
.Range(.Cells(1, 1), .Cells(1, lastCol)).Interior.Color = RGB(0, 112, 192)
.Range(.Cells(1, 1), .Cells(1, lastCol)).Font.Color = RGB(255, 255, 255)
' 数值格式
.Range(.Cells(2, 9), .Cells(lastRow, 9)).NumberFormat = "#,##0.00" ' 销售额
.Range(.Cells(2, 10), .Cells(lastRow, 10)).NumberFormat = "0.00%" ' 利润率
.Range(.Cells(2, 11), .Cells(lastRow, 11)).NumberFormat = "0.00%" ' 增长率
' 边框
.Range(.Cells(1, 1), .Cells(lastRow, lastCol)).Borders.LineStyle = xlContinuous
' 条件格式 - 评级
Dim ratingCol As Long
ratingCol = lastCol
.Range(.Cells(2, ratingCol), .Cells(lastRow, ratingCol)).FormatConditions.Add _
Type:=xlTextString, String:="A", TextOperator:=xlContains
.Range(.Cells(2, ratingCol), .Cells(lastRow, ratingCol)).FormatConditions(1).Interior.Color = RGB(146, 208, 80)
End With
End Sub
3.4 报表生成模块(modReport)
Option Explicit
'============================================
' 模块名称:modReport
' 功能描述:报表生成功能
'============================================
' 创建报表
Public Sub CreateReport()
On Error GoTo ErrorHandler
' 清空报表工作表
ClearSheet "报表"
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("报表")
' 生成报表标题
CreateReportHeader ws
' 生成汇总统计
CreateSummaryTable ws
' 生成明细数据
CreateDetailTable ws
' 格式化报表
FormatReport ws
WriteLog "报表生成完成"
Exit Sub
ErrorHandler:
Err.Raise Err.Number, "CreateReport", Err.Description
End Sub
' 创建报表标题
Private Sub CreateReportHeader(ws As Worksheet)
With ws
' 公司名称
.Range("A1").Value = GetConfigValue("CompanyName")
.Range("A1").Font.Size = 20
.Range("A1").Font.Bold = True
.Range("A1").Font.Color = RGB(0, 112, 192)
' 报表标题
.Range("A2").Value = "销售月度报表"
.Range("A2").Font.Size = 16
.Range("A2").Font.Bold = True
' 报表日期
.Range("A3").Value = "报表期间: " & Format(DateAdd("m", -1, Date), "yyyy年m月")
.Range("D3").Value = "生成时间: " & Format(Now, "yyyy-mm-dd hh:mm:ss")
' 合并单元格
.Range("A1:F1").Merge
.Range("A2:F2").Merge
End With
End Sub
' 创建汇总统计表
Private Sub CreateSummaryTable(ws As Worksheet)
Dim dataWS As Worksheet
Set dataWS = ThisWorkbook.Worksheets("处理数据")
Dim lastRow As Long
lastRow = dataWS.Cells(dataWS.Rows.Count, 1).End(xlUp).Row
' 汇总标题
ws.Range("A5").Value = "一、关键指标汇总"
ws.Range("A5").Font.Bold = True
ws.Range("A5").Font.Size = 14
' 汇总表头
ws.Range("A6:D6").Value = Array("指标", "数值", "单位", "同比变化")
ws.Range("A6:D6").Font.Bold = True
ws.Range("A6:D6").Interior.Color = RGB(200, 200, 255)
' 计算汇总数据
Dim totalSales As Double
Dim totalProfit As Double
Dim avgGrowthRate As Double
totalSales = Application.WorksheetFunction.Sum(dataWS.Range("I2:I" & lastRow))
Dim i As Long
Dim profitSum As Double
Dim growthSum As Double
Dim growthCount As Long
For i = 2 To lastRow
If IsNumeric(dataWS.Cells(i, 10).Value) Then
profitSum = profitSum + dataWS.Cells(i, 10).Value * dataWS.Cells(i, 9).Value
End If
If IsNumeric(dataWS.Cells(i, 11).Value) Then
growthSum = growthSum + dataWS.Cells(i, 11).Value
growthCount = growthCount + 1
End If
Next i
totalProfit = profitSum
avgGrowthRate = IIf(growthCount > 0, growthSum / growthCount, 0)
' 填充汇总数据
ws.Range("A7:D7").Value = Array("销售总额", Format(totalSales, "#,##0.00"), "万元", "+15.2%")
ws.Range("A8:D8").Value = Array("利润总额", Format(totalProfit, "#,##0.00"), "万元", "+12.8%")
ws.Range("A9:D9").Value = Array("平均增长率", Format(avgGrowthRate, "0.00%"), "-", "-")
ws.Range("A10:D10").Value = Array("记录数", lastRow - 1, "条", "-")
' 边框
ws.Range("A6:D10").Borders.LineStyle = xlContinuous
End Sub
' 创建明细数据表
Private Sub CreateDetailTable(ws As Worksheet)
Dim dataWS As Worksheet
Set dataWS = ThisWorkbook.Worksheets("处理数据")
' 明细标题
ws.Range("A12").Value = "二、明细数据"
ws.Range("A12").Font.Bold = True
ws.Range("A12").Font.Size = 14
' 复制数据
Dim lastRow As Long
Dim lastCol As Long
lastRow = dataWS.Cells(dataWS.Rows.Count, 1).End(xlUp).Row
lastCol = dataWS.Cells(1, dataWS.Columns.Count).End(xlToLeft).Column
' 复制表头
dataWS.Range(dataWS.Cells(1, 1), dataWS.Cells(1, lastCol)).Copy _
ws.Range("A13")
' 复制数据
dataWS.Range(dataWS.Cells(2, 1), dataWS.Cells(lastRow, lastCol)).Copy _
ws.Range("A14")
End Sub
' 格式化报表
Private Sub FormatReport(ws As Worksheet)
' 调整列宽
ws.Columns("A:L").AutoFit
' 设置打印区域
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
ws.PageSetup.PrintArea = "A1:L" & lastRow
' 页面设置
With ws.PageSetup
.Orientation = xlLandscape
.FitToPagesWide = 1
.CenterHeader = "&A"
.RightFooter = "第 &P 页,共 &N 页"
End With
End Sub
3.5 图表生成模块
Option Explicit
'============================================
' 创建图表
'============================================
Public Sub CreateCharts()
On Error GoTo ErrorHandler
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("图表")
' 清空工作表
ws.Cells.Clear
' 创建销售额趋势图
CreateSalesTrendChart ws
' 创建利润率分布图
CreateProfitDistributionChart ws
' 创建评级分布图
CreateRatingDistributionChart ws
WriteLog "图表生成完成"
Exit Sub
ErrorHandler:
Err.Raise Err.Number, "CreateCharts", Err.Description
End Sub
' 销售额趋势图
Private Sub CreateSalesTrendChart(ws As Worksheet)
Dim dataWS As Worksheet
Set dataWS = ThisWorkbook.Worksheets("处理数据")
Dim lastRow As Long
lastRow = dataWS.Cells(dataWS.Rows.Count, 1).End(xlUp).Row
' 创建图表
Dim cht As ChartObject
Set cht = ws.ChartObjects.Add(Left:=10, Top:=10, Width:=400, Height:=250)
With cht.Chart
.ChartType = xlColumnClustered
' 设置数据源
.SetSourceData Source:=dataWS.Range("H1:H" & lastRow & ",I1:I" & lastRow)
' 标题
.HasTitle = True
.ChartTitle.Text = "销售额趋势"
' X轴
.Axes(xlCategory).HasTitle = True
.Axes(xlCategory).AxisTitle.Text = "月份"
' Y轴
.Axes(xlValue).HasTitle = True
.Axes(xlValue).AxisTitle.Text = "销售额(万元)"
' 数据标签
.SeriesCollection(1).HasDataLabels = True
End With
End Sub
' 利润率分布图
Private Sub CreateProfitDistributionChart(ws As Worksheet)
Dim dataWS As Worksheet
Set dataWS = ThisWorkbook.Worksheets("处理数据")
Dim lastRow As Long
lastRow = dataWS.Cells(dataWS.Rows.Count, 1).End(xlUp).Row
' 创建图表
Dim cht As ChartObject
Set cht = ws.ChartObjects.Add(Left:=420, Top:=10, Width:=400, Height:=250)
With cht.Chart
.ChartType = xlLine
' 设置数据源
.SetSourceData Source:=dataWS.Range("H1:H" & lastRow & ",J1:J" & lastRow)
' 标题
.HasTitle = True
.ChartTitle.Text = "利润率趋势"
' 数据标签
.SeriesCollection(1).HasDataLabels = True
.SeriesCollection(1).DataLabels.NumberFormat = "0.0%"
End With
End Sub
' 评级分布饼图
Private Sub CreateRatingDistributionChart(ws As Worksheet)
Dim dataWS As Worksheet
Set dataWS = ThisWorkbook.Worksheets("处理数据")
' 统计各评级数量
Dim ratingCount As Object
Set ratingCount = CreateObject("Scripting.Dictionary")
Dim lastRow As Long
lastRow = dataWS.Cells(dataWS.Rows.Count, 1).End(xlUp).Row
Dim i As Long
Dim rating As String
For i = 2 To lastRow
rating = CStr(dataWS.Cells(i, 12).Value)
If ratingCount.Exists(rating) Then
ratingCount(rating) = ratingCount(rating) + 1
Else
ratingCount.Add rating, 1
End If
Next i
' 写入临时数据
ws.Range("N1").Value = "评级"
ws.Range("O1").Value = "数量"
Dim rowNum As Long
rowNum = 2
Dim key As Variant
For Each key In ratingCount.Keys
ws.Cells(rowNum, 14).Value = key
ws.Cells(rowNum, 15).Value = ratingCount(key)
rowNum = rowNum + 1
Next key
' 创建饼图
Dim cht As ChartObject
Set cht = ws.ChartObjects.Add(Left:=10, Top:=280, Width:=400, Height:=250)
With cht.Chart
.ChartType = xlPie
' 设置数据源
.SetSourceData Source:=ws.Range("N1:O" & rowNum - 1)
' 标题
.HasTitle = True
.ChartTitle.Text = "评级分布"
' 数据标签
.SeriesCollection(1).HasDataLabels = True
.SeriesCollection(1).DataLabels.ShowPercentage = True
End With
' 清除临时数据
ws.Range("N:O").Clear
End Sub
3.6 邮件发送模块(modEmail)
Option Explicit
'============================================
' 发送报表邮件
'============================================
Public Sub SendReportEmail()
On Error GoTo ErrorHandler
' 创建 Outlook 对象
Dim outlookApp As Object
Dim outlookMail As Object
On Error Resume Next
Set outlookApp = GetObject(, "Outlook.Application")
If outlookApp Is Nothing Then
Set outlookApp = CreateObject("Outlook.Application")
End If
On Error GoTo 0
' 创建邮件
Set outlookMail = outlookApp.CreateItem(0)
' 导出 PDF
Dim pdfPath As String
pdfPath = ExportReportToPDF()
With outlookMail
' 收件人
.To = GetConfigValue("EmailRecipients")
.CC = GetConfigValue("EmailCC")
' 主题
.Subject = "销售月度报表 - " & Format(DateAdd("m", -1, Date), "yyyy年m月")
' 正文
.Body = "各位领导:" & vbCrLf & vbCrLf & _
"附件为本月销售报表,请查收。" & vbCrLf & vbCrLf & _
"报表摘要:" & vbCrLf & _
"1. 本月销售总额:XXX 万元" & vbCrLf & _
"2. 本月利润总额:XXX 万元" & vbCrLf & _
"3. 同比增长:XX%" & vbCrLf & vbCrLf & _
"如有问题,请联系数据部。" & vbCrLf & vbCrLf & _
"此邮件由系统自动发送,请勿回复。"
' 添加附件
If pdfPath <> "" Then
.Attachments.Add pdfPath
End If
' 发送
.Send
End With
WriteLog "邮件发送完成"
Exit Sub
ErrorHandler:
WriteLog "邮件发送失败: " & Err.Description
End Sub
' 导出报表为 PDF
Private Function ExportReportToPDF() As String
Dim pdfPath As String
pdfPath = ThisWorkbook.Path & "\报表_" & Format(Date, "yyyymmdd") & ".pdf"
ThisWorkbook.Worksheets("报表").ExportAsFixedFormat _
Type:=xlTypePDF, _
Filename:=pdfPath, _
Quality:=xlQualityStandard
ExportReportToPDF = pdfPath
End Function
3.7 主菜单窗体(frmMain)
Option Explicit
'============================================
' 主菜单窗体代码
'============================================
Private Sub UserForm_Initialize()
Me.Caption = "自动化报表系统 v1.0"
' 居中显示
Me.StartUpPosition = 0
Me.Left = Application.Left + (Application.Width - Me.Width) / 2
Me.Top = Application.Top + (Application.Height - Me.Height) / 2
End Sub
' 生成报表按钮
Private Sub btnGenerateReport_Click()
Unload Me
GenerateReport
End Sub
' 数据导入按钮
Private Sub btnImportData_Click()
frmDataImport.Show
End Sub
' 系统配置按钮
Private Sub btnConfig_Click()
frmConfig.Show
End Sub
' 查看日志按钮
Private Sub btnViewLog_Click()
ThisWorkbook.Worksheets("日志").Activate
End Sub
' 退出按钮
Private Sub btnExit_Click()
Unload Me
End Sub
四、系统使用说明
4.1 安装与配置
- 打开 Excel,启用宏
- 首次运行时,在【配置】工作表设置参数
- 点击"生成报表"按钮开始
4.2 操作流程
启动系统 → 配置参数 → 导入数据 → 生成报表 → 发送邮件
4.3 注意事项
- 确保 Outlook 已配置邮箱账户
- 数据文件格式需符合模板要求
- 定期清理历史数据
五、项目总结与最佳实践
5.1 项目亮点
- 模块化设计:各功能独立,便于维护
- 完善的错误处理:日志记录,用户友好提示
- 用户界面友好:进度显示,操作简单
- 可扩展性强:易于添加新功能
5.2 最佳实践总结
| 实践 | 说明 |
|---|---|
| 模块化 | 按功能划分代码,便于维护和测试 |
| 错误处理 | 每个过程都有错误处理,记录日志 |
| 性能优化 | 批量操作,减少屏幕刷新 |
| 代码规范 | 统一命名,添加注释 |
| 用户体验 | 进度显示,友好提示 |
5.3 扩展方向
- 支持更多数据源(API、数据库)
- 添加数据分析和预测功能
- 集成 BI 工具
- 开发移动端查看
六、系列文章总结
恭喜你完成了《Excel VBA 从入门到精通》系列的学习!以下是整个系列的知识体系:
┌─────────────────────────────────────────────────────────┐
│ Excel VBA 知识体系 │
├─────────────────────────────────────────────────────────┤
│ │
│ 【基础篇】 │
│ ├── 宏录制与 VBE 环境 │
│ ├── 变量、数据类型与运算符 │
│ └── 程序流程控制 │
│ │
│ 【进阶篇】 │
│ ├── 数组与集合 │
│ ├── 过程与函数 │
│ └── Excel 对象模型 │
│ │
│ 【高级篇】 │
│ ├── 用户窗体设计 │
│ ├── 文件与数据处理 │
│ └── 错误处理与调试 │
│ │
│ 【实战篇】 │
│ └── 自动化报表系统开发 │
│ │
└─────────────────────────────────────────────────────────┘
💡 学习建议:编程是一门实践技能,多动手才能真正掌握。从简单的小工具开始,逐步挑战复杂项目。遇到问题多查阅文档、搜索解决方案,不断提升!
本文是《Excel VBA 从入门到精通》系列第十篇文章(完结篇)。
感谢你的阅读!如果这个系列对你有帮助,欢迎点赞、收藏、分享!有问题可以在评论区留言交流。
祝你 VBA 学习顺利,工作高效!