合租电费分摊系统实现

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace ElectricityBillCalculator
{
    public partial class MainForm : Form
    {
        // 人员信息
        private List<PersonMeter> persons;
        private TextBox txtRate;
        private TextBox txtTotalBill;
        private ComboBox cmbPeopleCount;
        private Button btnCalculate;
        private Label lblResult;
        private DataGridView dgvDetail;
        private Panel personPanel;
        private const double DEFAULT_TOTAL_BILL = 50.0;
        private string[] allNames = { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };

        public MainForm()
        {
            InitializeComponent();
            InitializePersons(4);
        }

        private void InitializeComponent()
        {
            this.Text = "电费计算器 - 合租电费分摊";
            this.Size = new System.Drawing.Size(980, 900);
            this.StartPosition = FormStartPosition.CenterScreen;
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.BackColor = Color.WhiteSmoke;

            // 标题
            Label lblTitle = new Label();
            lblTitle.Text = "📊 合租电费分摊系统";
            lblTitle.Location = new System.Drawing.Point(20, 15);
            lblTitle.Size = new System.Drawing.Size(930, 35);
            lblTitle.Font = new System.Drawing.Font("微软雅黑", 16, System.Drawing.FontStyle.Bold);
            lblTitle.ForeColor = Color.FromArgb(52, 73, 94);
            this.Controls.Add(lblTitle);

            // 人员面板
            personPanel = new Panel();
            personPanel.Location = new System.Drawing.Point(20, 60);
            personPanel.Size = new System.Drawing.Size(930, 380);
            personPanel.AutoScroll = true;
            personPanel.BackColor = Color.White;
            personPanel.BorderStyle = BorderStyle.FixedSingle;
            this.Controls.Add(personPanel);

            // 底部控制区
            int yBase = 460;

            // 第一行:电价、总电费、分摊人数和计算按钮
            // 电价
            Label lblRate = new Label();
            lblRate.Text = "电 价:";
            lblRate.Location = new System.Drawing.Point(30, yBase);
            lblRate.Size = new System.Drawing.Size(60, 30);
            lblRate.Font = new System.Drawing.Font("微软雅黑", 11, System.Drawing.FontStyle.Bold);
            lblRate.ForeColor = Color.FromArgb(41, 128, 185);
            this.Controls.Add(lblRate);

            txtRate = new TextBox();
            txtRate.Text = "0.5469";
            txtRate.Location = new System.Drawing.Point(95, yBase);
            txtRate.Size = new System.Drawing.Size(80, 30);
            txtRate.Font = new System.Drawing.Font("微软雅黑", 10);
            this.Controls.Add(txtRate);

            Label lblRateUnit = new Label();
            lblRateUnit.Text = "元/度";
            lblRateUnit.Location = new System.Drawing.Point(180, yBase);
            lblRateUnit.Size = new System.Drawing.Size(50, 30);
            lblRateUnit.Font = new System.Drawing.Font("微软雅黑", 10);
            this.Controls.Add(lblRateUnit);

            // 总电费
            Label lblTotalBill = new Label();
            lblTotalBill.Text = "💡 总电费:";
            lblTotalBill.Location = new System.Drawing.Point(240, yBase);
            lblTotalBill.Size = new System.Drawing.Size(85, 30);
            lblTotalBill.Font = new System.Drawing.Font("微软雅黑", 11, System.Drawing.FontStyle.Bold);
            lblTotalBill.ForeColor = Color.FromArgb(231, 76, 60);
            this.Controls.Add(lblTotalBill);

            txtTotalBill = new TextBox();
            txtTotalBill.Text = "50.00";
            txtTotalBill.Location = new System.Drawing.Point(330, yBase);
            txtTotalBill.Size = new System.Drawing.Size(80, 30);
            txtTotalBill.Font = new System.Drawing.Font("微软雅黑", 10);
            this.Controls.Add(txtTotalBill);

            Label lblBillUnit = new Label();
            lblBillUnit.Text = "元";
            lblBillUnit.Location = new System.Drawing.Point(415, yBase);
            lblBillUnit.Size = new System.Drawing.Size(30, 30);
            lblBillUnit.Font = new System.Drawing.Font("微软雅黑", 10);
            this.Controls.Add(lblBillUnit);

            // 分摊人数
            Label lblPeopleCount = new Label();
            lblPeopleCount.Text = "👥 分摊人数:";
            lblPeopleCount.Location = new System.Drawing.Point(455, yBase);
            lblPeopleCount.Size = new System.Drawing.Size(95, 30);
            lblPeopleCount.Font = new System.Drawing.Font("微软雅黑", 11, System.Drawing.FontStyle.Bold);
            lblPeopleCount.ForeColor = Color.FromArgb(142, 68, 173);
            this.Controls.Add(lblPeopleCount);

            cmbPeopleCount = new ComboBox();
            cmbPeopleCount.Location = new System.Drawing.Point(555, yBase);
            cmbPeopleCount.Size = new System.Drawing.Size(65, 30);
            cmbPeopleCount.Font = new System.Drawing.Font("微软雅黑", 10);
            cmbPeopleCount.DropDownStyle = ComboBoxStyle.DropDownList;
            cmbPeopleCount.Items.AddRange(new object[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" });
            cmbPeopleCount.SelectedIndex = 3;
            cmbPeopleCount.SelectedIndexChanged += CmbPeopleCount_SelectedIndexChanged;
            this.Controls.Add(cmbPeopleCount);

            // 计算按钮
            btnCalculate = new Button();
            btnCalculate.Text = "🧮 计算电费";
            btnCalculate.Location = new System.Drawing.Point(660, yBase - 5);
            btnCalculate.Size = new System.Drawing.Size(150, 40);
            btnCalculate.Font = new System.Drawing.Font("微软雅黑", 11, System.Drawing.FontStyle.Bold);
            btnCalculate.BackColor = Color.FromArgb(52, 152, 219);
            btnCalculate.ForeColor = Color.White;
            btnCalculate.FlatStyle = FlatStyle.Flat;
            btnCalculate.FlatAppearance.BorderSize = 0;
            btnCalculate.Click += BtnCalculate_Click;
            this.Controls.Add(btnCalculate);

            // 说明标签
            Label lblHint = new Label();
            lblHint.Text = "📌 用电量>0的人需承担单独计费,用电量=0的人仅平摊日常用电";
            lblHint.Location = new System.Drawing.Point(30, yBase + 50);
            lblHint.Size = new System.Drawing.Size(500, 25);
            lblHint.Font = new System.Drawing.Font("微软雅黑", 9);
            lblHint.ForeColor = Color.FromArgb(52, 73, 94);
            this.Controls.Add(lblHint);

            // 结果显示
            lblResult = new Label();
            lblResult.Text = "💰 每人应付:";
            lblResult.Location = new System.Drawing.Point(20, yBase + 90);
            lblResult.Size = new System.Drawing.Size(930, 35);
            lblResult.Font = new System.Drawing.Font("微软雅黑", 14, System.Drawing.FontStyle.Bold);
            lblResult.ForeColor = Color.FromArgb(39, 174, 96);
            this.Controls.Add(lblResult);

            // 明细表格
            dgvDetail = new DataGridView();
            dgvDetail.Location = new System.Drawing.Point(20, yBase + 135);
            dgvDetail.Size = new System.Drawing.Size(930, 205);
            dgvDetail.BackgroundColor = Color.White;
            dgvDetail.BorderStyle = BorderStyle.FixedSingle;
            dgvDetail.RowHeadersVisible = false;
            dgvDetail.AllowUserToAddRows = false;
            dgvDetail.AllowUserToDeleteRows = false;
            dgvDetail.ReadOnly = true;
            dgvDetail.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
            dgvDetail.Font = new System.Drawing.Font("微软雅黑", 10);
            dgvDetail.RowTemplate.Height = 38;
            dgvDetail.ColumnHeadersHeight = 40;
            dgvDetail.ColumnHeadersDefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 10.5f, System.Drawing.FontStyle.Bold);
            dgvDetail.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            dgvDetail.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            dgvDetail.GridColor = Color.FromArgb(220, 220, 220);
            dgvDetail.ScrollBars = ScrollBars.Vertical;

            // 创建列
            DataGridViewTextBoxColumn colName = new DataGridViewTextBoxColumn();
            colName.Name = "姓名";
            colName.HeaderText = "姓名";
            colName.Width = 70;
            colName.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            colName.FillWeight = 8;

            DataGridViewTextBoxColumn colUsage = new DataGridViewTextBoxColumn();
            colUsage.Name = "用电量";
            colUsage.HeaderText = "用电量(度)";
            colUsage.Width = 110;
            colUsage.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            colUsage.DefaultCellStyle.Format = "F2";
            colUsage.FillWeight = 12;

            DataGridViewTextBoxColumn colSeparate = new DataGridViewTextBoxColumn();
            colSeparate.Name = "单独计费";
            colSeparate.HeaderText = "单独计费(元)";
            colSeparate.Width = 120;
            colSeparate.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            colSeparate.DefaultCellStyle.Format = "F4";
            colSeparate.FillWeight = 13;

            DataGridViewTextBoxColumn colDaily = new DataGridViewTextBoxColumn();
            colDaily.Name = "日常平摊";
            colDaily.HeaderText = "日常平摊(元)";
            colDaily.Width = 120;
            colDaily.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            colDaily.DefaultCellStyle.Format = "F4";
            colDaily.FillWeight = 13;

            DataGridViewTextBoxColumn colTotal = new DataGridViewTextBoxColumn();
            colTotal.Name = "合计应付";
            colTotal.HeaderText = "合计应付(元)";
            colTotal.Width = 120;
            colTotal.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            colTotal.DefaultCellStyle.Format = "F4";
            colTotal.DefaultCellStyle.Font = new System.Drawing.Font("微软雅黑", 10.5f, System.Drawing.FontStyle.Bold);
            colTotal.DefaultCellStyle.ForeColor = Color.FromArgb(231, 76, 60);
            colTotal.FillWeight = 13;

            DataGridViewTextBoxColumn colFormula = new DataGridViewTextBoxColumn();
            colFormula.Name = "计算公式";
            colFormula.HeaderText = "计算公式";
            colFormula.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft; // 左对齐,方便阅读公式
            colFormula.DefaultCellStyle.Font = new System.Drawing.Font("Consolas", 9.5f); // 稍微减小字体以适应
            colFormula.DefaultCellStyle.ForeColor = Color.FromArgb(0, 0, 139);
            colFormula.FillWeight = 41;

            dgvDetail.Columns.Add(colName);
            dgvDetail.Columns.Add(colUsage);
            dgvDetail.Columns.Add(colSeparate);
            dgvDetail.Columns.Add(colDaily);
            dgvDetail.Columns.Add(colTotal);
            dgvDetail.Columns.Add(colFormula);

            // 设置列头颜色
            dgvDetail.EnableHeadersVisualStyles = false;
            dgvDetail.ColumnHeadersDefaultCellStyle.BackColor = Color.FromArgb(52, 73, 94);
            dgvDetail.ColumnHeadersDefaultCellStyle.ForeColor = Color.White;
            dgvDetail.ColumnHeadersDefaultCellStyle.SelectionBackColor = Color.FromArgb(52, 73, 94);

            this.Controls.Add(dgvDetail);
        }

        private void InitializePersons(int count)
        {
            personPanel.Controls.Clear();
            persons = new List<PersonMeter>();

            int x = 20, y = 25;

            for (int i = 0; i < count && i < allNames.Length; i++)
            {
                string name = allNames[i];
                var person = new PersonMeter(name);
                persons.Add(person);

                // 创建人员卡片
                GroupBox groupBox = new GroupBox();
                groupBox.Text = $"👤 {name}";
                groupBox.Location = new System.Drawing.Point(x, y);
                groupBox.Size = new System.Drawing.Size(430, 165);
                groupBox.Font = new System.Drawing.Font("微软雅黑", 10, System.Drawing.FontStyle.Bold);
                this.personPanel.Controls.Add(groupBox);

                // 起始表底
                Label lblStart = new Label();
                lblStart.Text = "起始表底:";
                lblStart.Location = new System.Drawing.Point(15, 40);
                lblStart.Size = new System.Drawing.Size(80, 25);
                lblStart.Font = new System.Drawing.Font("微软雅黑", 9);
                groupBox.Controls.Add(lblStart);

                TextBox txtStart = new TextBox();
                txtStart.Name = $"txtStart_{name}";
                txtStart.Location = new System.Drawing.Point(100, 38);
                txtStart.Size = new System.Drawing.Size(305, 25);
                txtStart.Font = new System.Drawing.Font("微软雅黑", 9);
                txtStart.Text = "0";
                groupBox.Controls.Add(txtStart);
                person.TxtStart = txtStart;

                // 结束表底
                Label lblEnd = new Label();
                lblEnd.Text = "结束表底:";
                lblEnd.Location = new System.Drawing.Point(15, 75);
                lblEnd.Size = new System.Drawing.Size(80, 25);
                lblEnd.Font = new System.Drawing.Font("微软雅黑", 9);
                groupBox.Controls.Add(lblEnd);

                TextBox txtEnd = new TextBox();
                txtEnd.Name = $"txtEnd_{name}";
                txtEnd.Location = new System.Drawing.Point(100, 73);
                txtEnd.Size = new System.Drawing.Size(305, 25);
                txtEnd.Font = new System.Drawing.Font("微软雅黑", 9);
                txtEnd.Text = "0";
                groupBox.Controls.Add(txtEnd);
                person.TxtEnd = txtEnd;

                // 用电量显示
                Label lblUsage = new Label();
                lblUsage.Text = "用电量:0.00 度";
                lblUsage.Name = $"lblUsage_{name}";
                lblUsage.Location = new System.Drawing.Point(15, 115);
                lblUsage.Size = new System.Drawing.Size(390, 30);
                lblUsage.Font = new System.Drawing.Font("微软雅黑", 10, System.Drawing.FontStyle.Regular);
                lblUsage.ForeColor = Color.FromArgb(44, 62, 80);
                groupBox.Controls.Add(lblUsage);
                person.LblUsage = lblUsage;

                // 布局:每行2个
                x += 450;
                if (x >= 910)
                {
                    x = 20;
                    y += 180;
                }
            }
        }

        private void CmbPeopleCount_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cmbPeopleCount.SelectedItem != null)
            {
                int count = Convert.ToInt32(cmbPeopleCount.SelectedItem.ToString());
                InitializePersons(count);
            }
        }

        private void BtnCalculate_Click(object sender, EventArgs e)
        {
            try
            {
                double rate = GetDouble(txtRate.Text);
                if (rate <= 0)
                {
                    MessageBox.Show("请输入有效的电价!", "输入错误", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }

                double totalCost = GetDouble(txtTotalBill.Text);
                if (totalCost <= 0)
                {
                    MessageBox.Show("请输入有效的总电费!", "输入错误", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }

                // 读取每个人的表底数据
                List<PersonData> dataList = new List<PersonData>();
                int totalPeople = 0;

                foreach (var person in persons)
                {
                    double start = GetDouble(person.TxtStart.Text);
                    double end = GetDouble(person.TxtEnd.Text);

                    if (end < start)
                    {
                        MessageBox.Show($"{person.Name} 的结束表底不能小于起始表底!", "输入错误", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                        return;
                    }

                    double usage = end - start;
                    totalPeople++;

                    bool isSeparateBilling = usage > 0;

                    dataList.Add(new PersonData
                    {
                        Name = person.Name,
                        Start = start,
                        End = end,
                        Usage = usage,
                        IsCooking = isSeparateBilling
                    });

                    string billingStatus = isSeparateBilling ? "⚡ 单独计费" : "仅平摊";
                    person.LblUsage.Text = $"用电量:{usage:F2} 度  ({billingStatus})";
                }

                if (totalPeople == 0)
                {
                    MessageBox.Show("没有人员数据!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                // 计算每个人的单独计费
                double totalSeparateBillingCost = 0;
                List<string> separateCostDetails = new List<string>(); // 存储每个单独计费的详细信息

                foreach (var data in dataList)
                {
                    if (data.IsCooking)
                    {
                        data.CookingCost = data.Usage * rate;
                        totalSeparateBillingCost += data.CookingCost;
                        separateCostDetails.Add($"{data.CookingCost:F4}");
                    }
                    else
                    {
                        data.CookingCost = 0;
                    }
                }

                // 构建所有单独计费的表达式
                string allSeparateCostsStr = string.Join("+", separateCostDetails);
                if (string.IsNullOrEmpty(allSeparateCostsStr))
                {
                    allSeparateCostsStr = "0.0000";
                }

                // 日常用电费用 = 总电费 - 所有单独计费
                double dailyElectricCost = totalCost - totalSeparateBillingCost;

                // 如果日常用电费用为负数,提示用户
                if (dailyElectricCost < 0)
                {
                    MessageBox.Show($"单独计费总计 {totalSeparateBillingCost:F2} 元已超过总电费 {totalCost:F2} 元!\n请检查电表读数或电价是否正确。",
                        "费用超支", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    dailyElectricCost = 0;
                }

                // 日常用电平摊给所有人
                double perPersonDaily = dailyElectricCost / totalPeople;

                // 计算每个人最终应付
                foreach (var data in dataList)
                {
                    data.TotalCost = data.CookingCost + perPersonDaily;
                }

                // 清空表格
                dgvDetail.Rows.Clear();

                // 填充表格
                foreach (var data in dataList)
                {
                    string formula;

                    if (data.IsCooking)
                    {
                        // 有单独计费的人:显示完整计算过程
                        // 格式:(总电费 - (所有单独计费之和)) ÷ 总人数 + 自己的单独计费
                        formula = $"({totalCost:F2} - ({allSeparateCostsStr})) ÷ {totalPeople} + {data.CookingCost:F4}";
                    }
                    else
                    {
                        // 没有单独计费的人:仅日常平摊
                        // 格式:(总电费 - (所有单独计费之和)) ÷ 总人数
                        formula = $"({totalCost:F2} - ({allSeparateCostsStr})) ÷ {totalPeople}";
                    }

                    dgvDetail.Rows.Add(
                        data.Name,
                        data.Usage,
                        data.CookingCost,
                        perPersonDaily,
                        data.TotalCost,
                        formula
                    );
                }

                // 突出显示每人应付总额
                string summary = "💰 每人应付:";
                foreach (var data in dataList)
                {
                    summary += $"{data.Name} {data.TotalCost:F2}元  ";
                }
                lblResult.Text = summary;

            }
            catch (Exception ex)
            {
                MessageBox.Show($"计算出错:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private double GetDouble(string text)
        {
            if (string.IsNullOrWhiteSpace(text))
                return 0;
            return Convert.ToDouble(text);
        }

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }

    // 人员电表类
    public class PersonMeter
    {
        public string Name { get; set; }
        public TextBox TxtStart { get; set; }
        public TextBox TxtEnd { get; set; }
        public Label LblUsage { get; set; }

        public PersonMeter(string name)
        {
            Name = name;
        }
    }

    // 人员数据类
    public class PersonData
    {
        public string Name { get; set; }
        public double Start { get; set; }
        public double End { get; set; }
        public double Usage { get; set; }
        public bool IsCooking { get; set; }
        public double CookingCost { get; set; }
        public double TotalCost { get; set; }
    }
}