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

Vue3 + Laravel实现发票数据按日期及发票号分组展示的问题

Vue3 + Laravel实现发票数据按日期及发票号分组展示的问题

嘿,看来你是想把从数据库查出来的扁平发票数据,转换成按日期、发票号层级分组的结构,然后在Vue3页面里优雅地展示出来对吧?我给你分前后端两部分来梳理解决方案,这样你能一步步搞定:

一、Laravel后端:先把扁平数据转成分组结构

你现在拿到的是join后的扁平数据,每个发票行都会重复发票的基本信息,我们需要先在后端把数据整理成日期→发票号→发票明细的层级结构,这样前端渲染会轻松很多。

示例代码(基于Eloquent或DB查询)

如果是用Eloquent关联查询:

use Carbon\Carbon;

// 假设你有Invoice模型和InvoiceItem模型,且已定义关联
$invoices = Invoice::with('invoiceItems')
    ->get()
    // 先按日期分组,格式化日期为Y-m-d格式
    ->groupBy(function ($invoice) {
        return Carbon::parse($invoice->datetime)->format('Y-m-d');
    })
    // 再把每个日期下的发票按发票号分组,整理明细和总金额
    ->map(function ($dailyInvoices) {
        return $dailyInvoices->groupBy('inv_num')->map(function ($invoiceGroup) {
            $invoice = $invoiceGroup->first();
            return [
                'inv_num' => $invoice->inv_num,
                'datetime' => $invoice->datetime,
                'total_amount' => $invoice->total_amount,
                'items' => $invoice->invoiceItems->map(function ($item) {
                    return [
                        'line_num' => $item->line_num,
                        'item_name' => $item->item_name,
                        'line_amount' => $item->line_amount
                    ];
                })
            ];
        });
    });

return response()->json($invoices);

如果是直接用DB查询的扁平数据:

use Carbon\Carbon;

$flatData = DB::table('invoices')
    ->join('invoice_items', 'invoices.id', '=', 'invoice_items.invoice_id')
    ->select(
        'invoices.inv_num', 
        'invoices.datetime', 
        'invoices.total_amount', 
        'invoice_items.line_num', 
        'invoice_items.item_name', 
        'invoice_items.line_amount'
    )
    ->get();

// 转换成分组结构
$groupedData = $flatData->groupBy(function ($item) {
    return Carbon::parse($item->datetime)->format('Y-m-d');
})->map(function ($dailyInvoices) {
    return $dailyInvoices->groupBy('inv_num')->map(function ($invoiceItems) {
        $firstItem = $invoiceItems->first();
        return [
            'inv_num' => $firstItem->inv_num,
            'datetime' => $firstItem->datetime,
            'total_amount' => $firstItem->total_amount,
            'items' => $invoiceItems->map(function ($item) {
                return [
                    'line_num' => $item->line_num,
                    'item_name' => $item->item_name,
                    'line_amount' => $item->line_amount
                ];
            })
        ];
    });
});

return response()->json($groupedData);

这样后端返回的就是按日期分组的嵌套结构,前端直接用就行。

二、Vue3前端:循环渲染分组数据

前端我们用<script setup>的组合式API来处理,先请求后端数据,然后嵌套循环渲染日期、发票、明细行。

示例代码

<template>
  <div class="invoice-wrapper">
    <!-- 循环日期分组 -->
    <div v-for="(dailyInvoices, dateStr) in groupedInvoices" :key="dateStr" class="daily-section">
      <h2>{{ formatDate(dateStr) }}</h2>

      <!-- 循环当前日期下的所有发票 -->
      <div v-for="(invoice, invNum) in dailyInvoices" :key="invNum" class="invoice-card">
        <h3>INV#{{ invNum }}</h3>
        
        <!-- 循环发票明细行 -->
        <div class="item-list">
          <div v-for="item in invoice.items" :key="item.line_num" class="item-row">
            <span>Line {{ item.line_num }}</span>
            <span>{{ item.item_name }}</span>
            <span>{{ item.line_amount }}</span>
          </div>
        </div>

        <!-- 发票总金额 -->
        <div class="invoice-total">
          <strong>Total Amount: {{ invoice.total_amount }}</strong>
        </div>
        <hr class="divider">
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'

const groupedInvoices = ref({})

// 格式化日期为友好展示格式
const formatDate = (dateStr) => {
  return new Date(dateStr).toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  })
}

onMounted(async () => {
  try {
    // 请求后端接口
    const res = await axios.get('/api/invoices')
    groupedInvoices.value = res.data
  } catch (err) {
    console.error('请求发票数据失败:', err)
  }
})
</script>

<style scoped>
.invoice-wrapper {
  max-width: 800px;
  margin: 20px auto;
  padding: 0 20px;
}

.daily-section {
  margin-bottom: 30px;
}

.invoice-card {
  margin: 15px 0 25px 20px;
  padding: 15px;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
}

.item-row {
  display: flex;
  gap: 30px;
  margin: 8px 0;
  padding-left: 10px;
}

.invoice-total {
  margin-top: 15px;
  text-align: right;
  font-size: 1.1em;
}

.divider {
  margin-top: 15px;
  border: none;
  border-top: 1px dashed #ccc;
}
</style>

几个注意点

  1. 日期格式化:你可以选择在后端用Carbon直接生成友好的日期字符串(比如$invoice->datetime->toFormattedDateString()),也可以在前端用toLocaleDateString适配不同语言环境。
  2. 数据准确性:因为扁平数据里发票的总金额会重复,分组时一定要取first()的总金额,避免重复计算。
  3. 样式调整:上面的CSS是基础样式,你可以根据自己的需求修改布局、颜色等。

备注:内容来源于stack exchange,提问作者Handsome.Paul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 11:34:33