Excel VBA 入门到精通(十):实战项目——自动化报表系统开发

🎯 本章目标:综合运用前九章所学知识,从零开始开发一个完整的自动化报表系统,掌握实际项目开发流程和最佳实践。


一、项目概述

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 安装与配置

  1. 打开 Excel,启用宏
  2. 首次运行时,在【配置】工作表设置参数
  3. 点击"生成报表"按钮开始

4.2 操作流程

复制代码
启动系统 → 配置参数 → 导入数据 → 生成报表 → 发送邮件

4.3 注意事项

  • 确保 Outlook 已配置邮箱账户
  • 数据文件格式需符合模板要求
  • 定期清理历史数据

五、项目总结与最佳实践

5.1 项目亮点

  1. 模块化设计:各功能独立,便于维护
  2. 完善的错误处理:日志记录,用户友好提示
  3. 用户界面友好:进度显示,操作简单
  4. 可扩展性强:易于添加新功能

5.2 最佳实践总结

实践 说明
模块化 按功能划分代码,便于维护和测试
错误处理 每个过程都有错误处理,记录日志
性能优化 批量操作,减少屏幕刷新
代码规范 统一命名,添加注释
用户体验 进度显示,友好提示

5.3 扩展方向

  • 支持更多数据源(API、数据库)
  • 添加数据分析和预测功能
  • 集成 BI 工具
  • 开发移动端查看

六、系列文章总结

恭喜你完成了《Excel VBA 从入门到精通》系列的学习!以下是整个系列的知识体系:

复制代码
┌─────────────────────────────────────────────────────────┐
│               Excel VBA 知识体系                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  【基础篇】                                              │
│   ├── 宏录制与 VBE 环境                                 │
│   ├── 变量、数据类型与运算符                            │
│   └── 程序流程控制                                      │
│                                                         │
│  【进阶篇】                                              │
│   ├── 数组与集合                                        │
│   ├── 过程与函数                                        │
│   └── Excel 对象模型                                    │
│                                                         │
│  【高级篇】                                              │
│   ├── 用户窗体设计                                      │
│   ├── 文件与数据处理                                    │
│   └── 错误处理与调试                                    │
│                                                         │
│  【实战篇】                                              │
│   └── 自动化报表系统开发                                │
│                                                         │
└─────────────────────────────────────────────────────────┘

💡 学习建议:编程是一门实践技能,多动手才能真正掌握。从简单的小工具开始,逐步挑战复杂项目。遇到问题多查阅文档、搜索解决方案,不断提升!


本文是《Excel VBA 从入门到精通》系列第十篇文章(完结篇)。

感谢你的阅读!如果这个系列对你有帮助,欢迎点赞、收藏、分享!有问题可以在评论区留言交流。

祝你 VBA 学习顺利,工作高效!

相关推荐
_oP_i2 小时前
Vibe coding介绍
ai
醇氧2 小时前
用 CC Switch (cc-sw) 配置 Claude Code 接入 阿里云百炼 (Dashscope)
人工智能·学习·阿里云·ai·云计算
阿洛学长2 小时前
2026年最佳AI提示词合集:ChatGPT、Claude、Gemini 提示词大全
人工智能·ai·chatgpt·ai作画
拾薪3 小时前
Brainstorming - 流程控制架构分析
ai·架构·superpower·brainstorming
余人于RenYu7 小时前
Claude + Figma MCP
前端·ui·ai·figma
小小李程序员10 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai
CodeCaptain12 小时前
【六】OpenClaw 从 TUI 切换到 Web 端完整方案
ubuntu·ai·openclaw
CodeCaptain14 小时前
【一】OpenClaw实战入门-环境搭建 (Ubuntu24.02 64位环境)
ai·openclaw
木斯佳16 小时前
前端八股文面经大全:腾讯CSIG实习面(2026-04-10)·面经深度解析
前端·ai·xss·埋点·实习面经