记账凭证管理页面demo(vue2+Fastapi)

一、项目demo

效果页面

静态页面下载:
链接:https://pan.quark.cn/s/2db47dbca87c

vue2+Fastapi:
链接:https://pan.quark.cn/s/1b817baf5fc5


链接里面有说明文档

功能实现:

1.会计科目实现下拉关联选择和过滤关联选择;

2.借方金额和贷方金额实现输入后映射金额的效果

3.头部日期控件选择后,完成对年月的数值显示;

4.合计功能大写,同时将借、贷金额数值汇总

二、页面效果

三、页面demo

可以直接复制粘贴(完全静态)

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>记账凭证管理</title>
    <!-- 引入 Element-UI 样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <style>
        body {
            font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
            padding: 20px;
        }

        .container {
            width: 90%;
            max-width: 1200px;
            margin: 0 auto;
        }

        .search-group {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }

        .el-table {
            margin-top: 20px;
        }

        .el-form-item {
            margin-bottom: 15px;
        }

        .operation-buttons {
            margin: 20px 0;
        }

        .voucher-item {
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }

        .info-row {
            display: flex;
            align-items: center;
            margin-bottom: 10px;
        }

        .info-row span {
            margin-right: 20px;
        }

        .voucher-table {
            width: 100%;
            border-collapse: collapse;
        }

        .voucher-table th,
        .voucher-table td {
            border: 1px solid #ccc;
            padding: 5px;
            text-align: center;
        }

        .total-row {
            font-weight: bold;
        }

        .footer-info {
            margin-top: 10px;
        }

        .footer-info span {
            margin-right: 20px;
        }

        .input-overlay {
            position: absolute;
            display: none;
            background-color: white;
            border: 1px solid #ccc;
            z-index: 100;
        }
    </style>
</head>

<body>
    <div id="app" class="container">

        <!-- 操作按钮 -->
        <div class="operation-buttons">
            <el-button type="primary" @click="openDialog('add')">录入凭证</el-button>
            <el-button type="danger" @click="batchDelete" :disabled="!selectedRows.length">批量删除</el-button>
        </div>

        <!-- 表格 -->
        <el-table
            :data="filteredVouchers"
            stripe
            @selection-change="handleSelectionChange"
        >
            <el-table-column type="selection" width="55"></el-table-column>
            <el-table-column prop="date" label="日期"></el-table-column>
            <el-table-column prop="voucherNumber" label="凭证字号"></el-table-column>
            <el-table-column label="摘要" >
                <template slot-scope="scope">
                    <div class="summary-container">
                      <div v-for="(item, index) in scope.row.items" :key="index">
                        {{ item.summary }}
                      </div>
                    </div>
                  </template>
            </el-table-column>
            <el-table-column label="科目">
                <template slot-scope="scope">
                    <div class="summary-container">
                      <div v-for="(item, index) in scope.row.items" :key="index">
                        {{ item.account }}
                      </div>
                    </div>
                  </template>
            </el-table-column>
            <el-table-column label="借方金额">
                <template slot-scope="scope">
                    {{ scope.row.items.reduce((sum, item) => {
                        const units = [100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1, 0.1, 0.01];
                        return sum + item.debitDigits.reduce((acc, digit, index) => acc + digit * units[index], 0);
                    }, 0) }}
                </template>
            </el-table-column>
            <el-table-column label="贷方金额">
                <template slot-scope="scope">
                    {{ scope.row.items.reduce((sum, item) => {
                        const units = [100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1, 0.1, 0.01];
                        return sum + item.creditDigits.reduce((acc, digit, index) => acc + digit * units[index], 0);
                    }, 0) }}
                </template>
            </el-table-column>
            <el-table-column prop="maker" label="制单人"></el-table-column>
            <el-table-column prop="reviewer" label="审核人"></el-table-column>
            <el-table-column label="操作" width="200">
                <template slot-scope="scope">
                    <el-button
                        type="primary"
                        size="mini"
                        @click="openDialog('edit', scope.row)"
                    >编辑</el-button>
                    <el-button
                        type="danger"
                        size="mini"
                        @click="deleteVoucher(scope.$index)"
                    >删除</el-button>
                </template>
            </el-table-column>
        </el-table>

        <!-- 表单对话框 -->
        <el-dialog
            title="凭证编辑"
            :visible.sync="dialogVisible"
            width="60%"
        >
            <!-- 单个凭证 -->
            <div class="voucher-item" v-for="(voucher, index) in [currentVoucher]" :key="index">
                <div class="info-row">
                    <span>凭证字:记</span>
                    <span>凭证号:{{ voucher.voucherNumber }}</span>
                    <el-date-picker v-model="voucher.date" type="date" placeholder="选择日期"></el-date-picker>
                </div>

                <table class="voucher-table">
                    <thead>
                        <tr>
                            <th style="width: 25%">摘要</th>
                            <th style="width: 25%">会计科目</th>
                            <th style="width: 25%" colspan="12">借方金额</th>
                            <th style="width: 25%" colspan="12">贷方金额</th>
                        </tr>
                        <tr>
                            <th></th>
                            <th></th>
                            <th></th>
                            <th>亿</th>
                            <th>千</th>
                            <th>百</th>
                            <th>十</th>
                            <th>万</th>
                            <th>千</th>
                            <th>百</th>
                            <th>十</th>
                            <th>元</th>
                            <th>角</th>
                            <th>分</th>

                            <th></th>
                            <th>亿</th>
                            <th>千</th>
                            <th>百</th>
                            <th>十</th>
                            <th>万</th>
                            <th>千</th>
                            <th>百</th>
                            <th>十</th>
                            <th>元</th>
                            <th>角</th>
                            <th>分</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr v-for="(item, itemIndex) in voucher.items" :key="itemIndex">
                            <td><input v-model="item.summary" style="width: 100%; height: 100%; border: none; outline:none;"></td>
                            <td><input v-model="item.account" style="width: 100%; height: 100%; border: none; outline:none;"></td>
                            <!-- 分割格子用于美观 -->
                            <td style="background-color: #F5F7FA;"></td>
                            <!-- 对应单位:亿 千 百 十 万 千 百 十 元 角 分  -->
                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #f8e4d7;">{{ item.debitDigits[0] }}</td>
                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #f8e4d7;">{{ item.debitDigits[1] }}</td>
                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #f8e4d7;">{{ item.debitDigits[2] }}</td>

                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #f7f8cb;">{{ item.debitDigits[3] }}</td>
                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #f7f8cb;">{{ item.debitDigits[4] }}</td>
                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #f7f8cb;">{{ item.debitDigits[5] }}</td>

                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #e0ebf7;">{{ item.debitDigits[6] }}</td>
                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #e0ebf7;">{{ item.debitDigits[7] }}</td>
                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #e0ebf7;">{{ item.debitDigits[8] }}</td>

                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #cff6ee;">{{ item.debitDigits[9] }}</td>
                            <td @click="showInput(voucher, item, 'debit')" style="background-color: #cff6ee;">{{ item.debitDigits[10] }}</td>

                            <!-- 分割格子用于美观 -->
                            <td style="background-color: #F5F7FA;"></td>
                            <!-- 对应单位:亿 千 百 十 万 千 百 十 元 角 分  -->
                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #f8e4d7;">{{ item.creditDigits[0] }}</td>
                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #f8e4d7;">{{ item.creditDigits[1] }}</td>
                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #f8e4d7;">{{ item.creditDigits[2] }}</td>

                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #f7f8cb;">{{ item.creditDigits[3] }}</td>
                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #f7f8cb;">{{ item.creditDigits[4] }}</td>
                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #f7f8cb;">{{ item.creditDigits[5] }}</td>

                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #e0ebf7;">{{ item.creditDigits[6] }}</td>
                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #e0ebf7;">{{ item.creditDigits[7] }}</td>
                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #e0ebf7;">{{ item.creditDigits[8] }}</td>

                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #cff6ee;">{{ item.creditDigits[9] }}</td>
                            <td @click="showInput(voucher, item, 'credit')" style="background-color: #cff6ee;">{{ item.creditDigits[10] }}</td>
                        </tr>
                        <tr class="total-row">
                            <td colspan="2">合计</td>
                            <td colspan="12">
                                <div class="amount-cell">
                                    {{ formatAmount(voucher.totalDebit) }}
                                </div>
                            </td>
                            <td colspan="12">
                                <div class="amount-cell">
                                    {{ formatAmount(voucher.totalCredit) }}
                                </div>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <div class="footer-info">
                    <span>制单时间:{{ voucher.makeTime }}</span>
                    <span>制单人:{{ voucher.maker }}</span>
                    <span>备注:{{ voucher.remark }}</span>
                </div>
            </div>
            <div class="input-overlay" ref="inputOverlay" @click.away="hideInput">
                <!-- // 设置输入框的宽度和高度 -->
                <input style="border: none; outline: none;" v-model="inputValue" @keydown.enter="submitInput">
            </div>
            <div slot="footer" class="dialog-footer">
                <el-button @click="dialogVisible = false">取消</el-button>
                <el-button type="primary" @click="saveVoucher">保存</el-button>
            </div>
        </el-dialog>
    </div>

    <!-- 引入 Vue 和 Element-UI 脚本 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    searchDate: null,
                    vouchers: [
                        {
                            id: 1,
                            date: '2025-03-15',
                            voucherNumber: '记-001',
                            maker: '张三',
                            reviewer: '李四',
                            items: [
                                {
                                    summary: '业务费',
                                    account: '管理费_招待费',
                                    debitDigits: [0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0],
                                    creditDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                                },
                                {
                                    summary: '业务费',
                                    account: '管理费_水电费',
                                    debitDigits: [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
                                    creditDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                                },
                            ],
                            totalDebit: 0,
                            totalCredit: 0,
                            makeTime: '2025-3-16',
                            remark: '无'
                        },
                        {
                            id: 2,
                            date: '2025-03-16',
                            voucherNumber: '记-002',
                            maker: '王五',
                            reviewer: '王麻子',
                            items: [
                                {
                                    summary: '业务费',
                                    account: '管理费_招待费',
                                    debitDigits: [0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0],
                                    creditDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                                },
                                {
                                    summary: '业务费',
                                    account: '管理费_水电费',
                                    debitDigits: [0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0],
                                    creditDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                                },
                            ],
                            totalDebit: 0,
                            totalCredit: 0,
                            makeTime: '2025-3-16',
                            remark: '无'
                        }
                    ],
                    dialogVisible: false,
                    currentVoucher: {},
                    selectedRows: [],
                    inputValue: '',
                    currentItem: null,
                    currentType: null
                };
            },
            computed: {
                filteredVouchers() {
                    if (this.searchDate) {
                        return this.vouchers.filter(v => new Date(v.date).toDateString() === new Date(this.searchDate).toDateString());
                    }
                    return this.vouchers;
                }
            },
            methods: {
                openDialog(type, row = null) {
                    this.currentVoucher = type === 'add' ? {
                        id: Date.now(),
                        date: new Date().toISOString().split('T')[0],
                        voucherNumber: '',
                        maker: '',
                        reviewer: '',
                        items: [
                            {
                                summary: '',
                                account: '',
                                debitDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                                creditDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                            },
                            {
                                summary: '',
                                account: '',
                                debitDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                                creditDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                            },
                            {
                                summary: '',
                                account: '',
                                debitDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                                creditDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                            },
                            {
                                summary: '',
                                account: '',
                                debitDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                                creditDigits: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                            }
                        ],
                        totalDebit: 0,
                        totalCredit: 0,
                        makeTime: new Date().toISOString().split('T')[0],
                        remark: '无'
                    } : { ...row };
                    this.dialogVisible = true;
                },

                saveVoucher() {
                    if (this.currentVoucher.id) {
                        // 更新操作
                        const index = this.vouchers.findIndex(v => v.id === this.currentVoucher.id);
                        this.vouchers.splice(index, 1, this.currentVoucher);
                    } else {
                        // 新增操作
                        this.currentVoucher.id = Date.now();
                        this.vouchers.push(this.currentVoucher);
                    }
                    this.dialogVisible = false;
                    this.$message.success('操作成功');
                },

                deleteVoucher(index) {
                    this.$confirm('确认删除该凭证吗?', '提示', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                    }).then(() => {
                        this.vouchers.splice(index, 1);
                        this.$message.success('删除成功');
                    });
                },

                batchDelete() {
                    if (this.selectedRows.length === 0) return;
                    this.$confirm(`确认删除${this.selectedRows.length}条记录吗?`, '提示', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                    }).then(() => {
                        this.vouchers = this.vouchers.filter(v => !this.selectedRows.includes(v));
                        this.$message.success(`成功删除${this.selectedRows.length}条记录`);
                        this.selectedRows = [];
                    });
                },

                searchVouchers() {
                    // 这里可以添加实际的搜索逻辑(如调用接口)
                    this.$message.info('搜索功能已触发');
                },

                handleSelectionChange(rows) {
                    this.selectedRows = rows;
                },
                // 统计账单金额
                calculateTotal(voucher) {
                    let totalDebit = 0;
                    let totalCredit = 0;
                    const units = [100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1, 0.1, 0.01];
                    voucher.items.forEach(item => {
                        totalDebit += item.debitDigits.reduce((sum, digit, index) => {
                            return sum + digit * units[index];
                        }, 0);
                        totalCredit += item.creditDigits.reduce((sum, digit, index) => {
                            return sum + digit * units[index];
                        }, 0);
                    });
                    voucher.totalDebit = totalDebit;
                    voucher.totalCredit = totalCredit;
                },
                // 这里统计账单金额  把开头为0的全部去掉,在数组的倒数第二位加上小数点
                /**
                 * 格式化金额
                 * @param {number} amount - 需要格式化的金额数字
                 * @returns {string} - 格式化后的金额字符串
                 */
                formatAmount(amount) {
                    if (amount === undefined || amount === null) {
                        return '0.00';
                    }
                    const [integer, decimal] = amount.toFixed(2).split('.');
                    return `${integer.replace(/^0+/, '') || '0'}.${decimal}`;
                },
                // 显示输入框
                showInput(voucher, item, type) {
                    this.currentVoucher = voucher;
                    this.currentItem = item;
                    this.currentType = type;
                    this.inputValue = '';
                    // 用个按钮,并把按钮变成input
                    const inputOverlay = this.$refs.inputOverlay;
                    const inputElement = inputOverlay.querySelector('input');
                    let targetCell;

                    if (type == 'debit') {
                        console.log('showInput', type);
                        targetCell = event.currentTarget.parentElement.querySelectorAll('td')[3];
                    } else {
                        console.log('showInput', type);
                        targetCell = event.currentTarget.parentElement.querySelectorAll('td')[15];
                    }

                    inputOverlay.style.display = 'block';
                    const rect = targetCell.getBoundingClientRect();
                    inputOverlay.style.left = rect.left + 'px';
                    inputOverlay.style.top = rect.top + 'px';
                    // 自动聚焦输入框,进入输入状态
                    inputElement.focus();
                    inputElement.style.outline = 'none';
                    // 获取 单元格的长度和宽度,动态设置宽高  宽度 = 单元格的宽度*11,长度 = 单元格的高度
                    inputElement.style.width = rect.width * 11 + 17 + 'px';
                    inputElement.style.height = rect.height + 'px';

                },
                hideInput() {
                    this.$refs.inputOverlay.style.display = 'none';
                },
                submitInput() {
                    const amount = parseFloat(this.inputValue);  // 转换为数字类型
                    if (!isNaN(amount)) {
                        if (amount === undefined || amount === null) {
                            return '0.00';
                        }
                        let digits = amount.toFixed(2).toString().replace('.', '').padStart(11, '0').split('').map(Number);
                        if (this.currentType === 'debit') {
                            this.currentItem.debitDigits = digits;
                        } else {
                            this.currentItem.creditDigits = digits;
                        }
                        this.calculateTotal(this.currentVoucher);
                    }
                    this.hideInput();  // 隐藏输入框
                }
            }
        });
    </script>
</body>

</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

peter123123123123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值