You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js发票金额双向绑定及折扣、反向计算功能实现咨询

发票计算功能实现方案

核心计算逻辑

正向计算(修改数量/单价/税率/折扣时自动更新实付净额)

  1. 先计算无折扣含税总价:无折扣含税总价 = 数量 × 单价 × (1 + 增值税率 / 100)
  2. 再计算折扣后实付净额:
    • 固定金额折扣:实付净额 = 无折扣含税总价 - 折扣金额
    • 百分比折扣:实付净额 = 无折扣含税总价 × (1 - 折扣比例 / 100)

反向计算(修改实付净额时自动更新单价)

  1. 先根据实付净额反推无折扣含税总价:
    • 固定金额折扣:无折扣含税总价 = 实付净额 + 折扣金额
    • 百分比折扣:无折扣含税总价 = 实付净额 / (1 - 折扣比例 / 100)
  2. 计算单价:单价 = 无折扣含税总价 / (数量 × (1 + 增值税率 / 100))(数量为0时单价直接设为0,避免除零错误)

修改内容说明

  1. HTML部分
    • 所有数值输入框修改为type="number",避免非数字输入
    • 数量、单价、税率、折扣输入框添加输入事件绑定,触发正向计算
    • 实付净额输入框添加输入事件绑定,触发反向计算
    • 折扣输入框补充v-model绑定,修正百分比选项显示符号
  2. JS部分
    • 新增正向计算方法calcNetTotal,处理实付净额自动更新逻辑
    • 新增反向计算方法calcUnitPrice,处理单价自动更新逻辑
    • 新增数值校验逻辑,避免非法输入导致NaN问题

完整可运行代码

JS代码

const app = new Vue({
    el: "#app",
    data: {
        invoiceItem: [
            {
                name: "",
                quantity: 0,
                unit_price: 0,
                vat_rate: 18,
                net_total: 0,
                showDesc: false,
                showDiscount: false,
                description: '',
                discount_value: 0,
                discount_rate:'dollar'
            },
        ],
    },
    methods: {
        addInvoice() {
            this.invoiceItem.push({
                name: "",
                quantity: 0,
                unit_price: 0,
                vat_rate: 18,
                net_total: 0,
                showDesc: false,
                showDiscount: false,
                description: '',
                discount_value: 0,
                discount_rate:'dollar'
            });
        },
        removeIncoiceItem(index) {
            this.invoiceItem.splice(index, 1);
        },
        // 数值安全转换,避免NaN
        safeNumber(val) {
            const num = Number(val);
            return isNaN(num) ? 0 : num;
        },
        // 正向计算:更新实付净额
        calcNetTotal(item) {
            const quantity = this.safeNumber(item.quantity);
            const unitPrice = this.safeNumber(item.unit_price);
            const vatRate = this.safeNumber(item.vat_rate);
            const discountVal = this.safeNumber(item.discount_value);
            // 计算无折扣含税总价
            let totalWithoutDiscount = quantity * unitPrice * (1 + vatRate / 100);
            // 计算折扣后金额,最低为0
            if (item.discount_rate === 'dollar') {
                item.net_total = Math.max(0, totalWithoutDiscount - discountVal).toFixed(2);
            } else {
                item.net_total = Math.max(0, totalWithoutDiscount * (1 - discountVal / 100)).toFixed(2);
            }
        },
        // 反向计算:更新单价
        calcUnitPrice(item) {
            const quantity = this.safeNumber(item.quantity);
            const vatRate = this.safeNumber(item.vat_rate);
            const discountVal = this.safeNumber(item.discount_value);
            const netTotal = this.safeNumber(item.net_total);
            // 数量为0直接返回0,避免除零错误
            if (quantity === 0) {
                item.unit_price = 0;
                return;
            }
            // 反推无折扣含税总价
            let totalWithoutDiscount;
            if (item.discount_rate === 'dollar') {
                totalWithoutDiscount = netTotal + discountVal;
            } else {
                totalWithoutDiscount = discountVal >= 100 ? 0 : netTotal / (1 - discountVal / 100);
            }
            // 计算单价
            item.unit_price = (totalWithoutDiscount / (quantity * (1 + vatRate / 100))).toFixed(2);
        }
    },
});

HTML代码

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<section class="container">
    <div class="row">
        <table class="table">
            <thead class="thead-dark">
            <tr>
                <th style="width:17%">商品名称</th>
                <th style="width:14%">数量</th>
                <th style="width:14%">单价</th>
                <th style="width:15%">增值税率</th>
                <th style="width:14%">实付净额</th>
                <th style="width:15%">操作</th>
            </tr>
            </thead>
        </table>
        <div v-for="(item, index) in invoiceItem" :key="index" style="margin-bottom: 10px">
            <div class="row">
                <div class="col-md-2">
                    <input type="text" class="form-control" v-model="item.name">
                </div>
                <div class="col-md-2">
                    <input type="number" class="form-control" v-model="item.quantity" @input="calcNetTotal(item)">
                </div>
                <div class="col-md-2">
                    <input type="number" class="form-control" v-model="item.unit_price" @input="calcNetTotal(item)">
                </div>
                <div class="col-md-1">
                    <select class="form-select" v-model="item.vat_rate" @change="calcNetTotal(item)">
                        <option value="18">%18</option>
                        <option value="8">%8</option>
                        <option value="1">%1</option>
                        <option value="0">%0</option>
                    </select>
                </div>
                <div class="col-md-2">
                    <input type="number" class="form-control" v-model="item.net_total" @input="calcUnitPrice(item)">
                </div>
                <div class="col-md-3">
                    <button
                        class="btn btn-primary me-1"
                        v-if="!item.showDesc" @click="item.showDesc = !item.showDesc">添加备注
                    </button>
                    <button
                        class="btn btn-secondary me-1"
                        v-if="!item.showDiscount" @click="item.showDiscount = !item.showDiscount">添加折扣
                    </button>
                    <button class="btn btn-danger" @click="removeIncoiceItem(index)">删除</button>
                </div>
                <div class="col-md-4 mt-2" v-if="item.showDesc">
                    <input type="text" class="form-control" placeholder="备注信息" v-model="item.description">
                </div>
                <div class="col-md-4 mt-2" v-if="item.showDiscount">
                    <div class="input-group">
                        <input type="number" class="form-control" placeholder="折扣数值" v-model="item.discount_value" @input="calcNetTotal(item)">
                        <select class="form-select" v-model="item.discount_rate" @change="calcNetTotal(item)">
                            <option value="dollar">USD</option>
                            <option value="percent">%</option>
                        </select>
                    </div>
                </div>
            </div>
            <hr>
        </div>
        <hr>
        <div style="margin-top:10px">
            <button class="btn btn-warning" @click="addInvoice()">新增商品行</button>
        </div>
    </div>
</section>
</div>

内容的提问来源于stack exchange,提问作者Selaminko Elam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 08:39:02