PAYATHON 2026

Visual Basic 中如何计算用户的总付款金额?

支付小周

结论

代码没有输出,原因主要有两个:程序没有累计 5 次输入,也没有更新 hourCounter 和 hourTotal。另外,原来的条件只在数值正好等于 15、25 或 40 时才会成立。总时数如果是 20 或 35,就不会进入任何分支。

可以把输入次数和累计时数定义为窗体级变量。每次单击按钮后,程序按以下顺序处理:

  1. 将文本框内容转换为数值;
  2. 检查时数是否有效且不小于零;
  3. 检查累计时数是否会超过 40;
  4. 累加时数,并将输入次数加 1;
  5. 完成第 5 次输入后,计算并显示付款金额。

为什么原代码没有得到结果

没有累计输入值

现有代码缺少下面这样的累加操作:

hourTotal += enteredHours
hourCounter += 1

所以程序既没有记录用户输入了多少次,也没有计算 5 次输入的总时数。

相等判断过于严格

下面的条件只会在文本框内容正好为 15 时执行:

If Enterhourstextbox.Text = 15 Then

另外两个条件也只接受 hourCounter 正好等于 25 或 40:

If hourCounter = 25 Then
If hourCounter = 40 Then

如果 hourCounter 表示输入次数,它的取值应该是 1 到 5,不能用来和工时阈值比较。如果它表示累计工时,这个变量名很容易让人误解,而且仍然不能只判断三个精确值。

文本没有显式转换为数字

Enterhourstextbox.Text 的类型是 String。直接将它与数字比较,需要依赖隐式类型转换。启用 Option Strict On 后,这种写法无法通过编译。

更稳妥的方式是使用 Decimal.TryParse:

Dim enteredHours As Decimal

If Not Decimal.TryParse(Enterhourstextbox.Text, enteredHours) Then
    MessageBox.Show("请输入有效的工作时数。")
    Return
End If

变量需要在多次单击之间保留

如果在按钮事件内部定义 hourTotal 和输入次数,每次单击按钮时,这两个变量都会重新初始化。应当把它们定义在窗体类中:

Private hourTotal As Decimal = 0D
Private entryCount As Integer = 0

完整实现示例

下面的代码采用这套付款规则:

  • 总时数不超过 15 小时:每小时 9;
  • 总时数超过 15、但不超过 25 小时:每小时 13;
  • 总时数超过 25、但不超过 40 小时:每小时 20。

最终总时数会决定一个统一的小时费率。

Option Strict On

Public Class TimeEntry

    Private Const MaximumEntries As Integer = 5
    Private Const MaximumHours As Decimal = 40D

    Private hourTotal As Decimal = 0D
    Private entryCount As Integer = 0

    Private Sub SubmitTimeButton_Click(
        sender As Object,
        e As EventArgs
    ) Handles SubmitTimeButton.Click

        Dim enteredHours As Decimal

        ' 将文本转换为数值
        If Not Decimal.TryParse(Enterhourstextbox.Text, enteredHours) Then
            MessageBox.Show("请输入有效的工作时数。")
            Enterhourstextbox.SelectAll()
            Enterhourstextbox.Focus()
            Return
        End If

        ' 不接受负数
        If enteredHours < 0D Then
            MessageBox.Show("工作时数不能为负数。")
            Enterhourstextbox.SelectAll()
            Enterhourstextbox.Focus()
            Return
        End If

        ' 防止输入超过 5 次
        If entryCount >= MaximumEntries Then
            MessageBox.Show("已经完成 5 次工时输入。")
            Return
        End If

        ' 检查累计时数是否超过 40
        If hourTotal + enteredHours > MaximumHours Then
            Dim remainingHours As Decimal = MaximumHours - hourTotal

            MessageBox.Show(
                $"累计工作时数不能超过 40。当前最多还可以输入 {remainingHours} 小时。"
            )

            Enterhourstextbox.SelectAll()
            Enterhourstextbox.Focus()
            Return
        End If

        ' 保存本次输入
        hourTotal += enteredHours
        entryCount += 1

        TimeRecordsListBox.Items.Add(
            $"第 {entryCount} 次:{enteredHours} 小时"
        )

        Enterhourstextbox.Clear()
        Enterhourstextbox.Focus()

        ' 收集完 5 次输入后计算付款
        If entryCount = MaximumEntries Then
            Dim hourlyRate As Decimal

            If hourTotal <= 15D Then
                hourlyRate = 9D
            ElseIf hourTotal <= 25D Then
                hourlyRate = 13D
            Else
                hourlyRate = 20D
            End If

            Dim payment As Decimal = hourTotal * hourlyRate

            getpaymentlabel.Text =
                $"总时数:{hourTotal};付款金额:{payment:C2}"

            SubmitTimeButton.Enabled = False
        Else
            getpaymentlabel.Text =
                $"已输入 {entryCount}/5 次,当前累计 {hourTotal} 小时。"
        End If
    End Sub

    Private Sub CleartimeButton_Click(
        sender As Object,
        e As EventArgs
    ) Handles CleartimeButton.Click

        TimeRecordsListBox.Items.Clear()
        getpaymentlabel.Text = String.Empty
        Enterhourstextbox.Clear()

        ' 同时重置内部状态
        hourTotal = 0D
        entryCount = 0

        SubmitTimeButton.Enabled = True
        Enterhourstextbox.Focus()
    End Sub

End Class

如果费率采用分段计算

付款规则还需要进一步确认。上面的代码会根据最终总时数选择一个统一费率。例如,总时数为 30 时,付款金额为:

30 × 20

如果规则是前 15 小时按 9 计算,接下来的 10 小时按 13 计算,超过 25 小时的部分按 20 计算,就需要采用分段计费。可以把计算过程单独写成一个函数:

Private Function CalculatePayment(totalHours As Decimal) As Decimal
    If totalHours <= 15D Then
        Return totalHours * 9D
    ElseIf totalHours <= 25D Then
        Return (15D * 9D) +
               ((totalHours - 15D) * 13D)
    Else
        Return (15D * 9D) +
               (10D * 13D) +
               ((totalHours - 25D) * 20D)
    End If
End Function

完成第 5 次输入后,再调用这个函数:

Dim payment As Decimal = CalculatePayment(hourTotal)
getpaymentlabel.Text =
    $"总时数:{hourTotal};付款金额:{payment:C2}"

注意事项

  • hourTotal 保存累计工时,entryCount 保存输入次数,不能混用。
  • 清空界面时,也要重置这两个变量。只清空控件不会改变程序内部保存的状态。
  • 使用 Decimal 保存工时和金额,可以处理 7.5 这类带小数的时数。
  • payment:C2 会根据当前系统的区域设置显示货币符号。如果需要显示特定货币,应指定相应格式或直接添加货币单位。
  • 建议启用 Option Strict On,以便尽早发现字符串与数字之间的错误比较及隐式转换。

备注:内容仅供参考。