-1

i want to create a line with total from each group of items in a list like this:

Model

So i did this to group by Car:

if (val == 2) {
  this.dadosResumo.sort((a, b) => (a.carro > b.carro ? 1 : -1))

and i'm using v-data-table to render the itens from dadosResumo.

but idk how to create a line in each group with the totals.

the list:

[
 carro: '11016', number: '1', quantity: '4', total: '2'},
 carro: '11016', number: '2', quantity: '3', total: '4'},
 carro: '1122', number: '2', quantity: '4', total: '5'},
 carro: '1122', number: '1', quantity: '1', total: '4'},
 carro: '1133', number: '1', quantity: '2', total: '3'}, 
]

i tried to create an map to add line with if but not worked.


EDITED

i was thinking and the best way that i saw is to put the Subtotal inside the list, like this, because i use the list to create an pdf, anyway to create an map to add the subtotal inside? the list should be:

[
     {carro: '11016', number: '1', quantity: '4', total: '2'},
     {carro: '11016', number: '2', quantity: '3', total: '4'},
     **{carro: 'Subtotal', number: '', quantity: '7', total: '6'},**
     {carro: '1122', number: '2', quantity: '4', total: '5'},
     {carro: '1122', number: '1', quantity: '1', total: '4'},
     **{carro: 'Subtotal', number: '', quantity: '5', total: '9'},**
     {carro: '1133', number: '1', quantity: '2', total: '3'}, 
     **{carro: 'Subtotal', number: '', quantity: '2', total: '3},**
]

2 Answers2

1

First you should calculate all 'Subtotal' and put it in a list with the car code (for example i call ListSubtotal), then create an inner sublist according to the car code of the outer list:

#List Subtotal
    For( var x in ListSubtotal) { 
      if(Car == x.car)
         render all child in here
}
0

This is somewhat hard to do with v-data-table. Not just writing the code so that it looks like in your image after load, but also to have it keep making sense when you interact with the table.

For example, the table in the image seems to be sorted by car - if I sort by date, where would the subtotals go? Or if I filter the table, should the subtotals recalculate without the filtered rows?

If it does not make sense to interact with the table that way and you'll disable sorting, filtering, pagination anyway, I am going to go ahead and say you will be better off with a v-simple-table (or v-table in Vuetify 3). It will give you more flexibility for sacrificing functionality you don't need. It is pretty straight-forward then:

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data: {
    items: [
     {carro: '11016', number: '1', quantity: '4', total: '2'},
     {carro: '11016', number: '2', quantity: '3', total: '4'},
     {carro: '1122', number: '2', quantity: '4', total: '5'},
     {carro: '1122', number: '1', quantity: '1', total: '4'},
     {carro: '1133', number: '1', quantity: '2', total: '3'}, 
    ]
  },
  computed: {
    itemsWithTotal() {
      const list = []
      let sum = 0
      for(let i = 0; i < this.items.length; i++){
        const item = this.items[i]
        list.push({type: 'item', ...item, id: list.length})
        sum += Number(item.total)
        if(item.carro !== this.items[i+1]?.carro){
          list.push({type: 'subtotal', sum, id: list.length})
          sum = 0
        }
      }
      return list
    }
  }
  
})
tr.subtotal{
  background: grey;
  color: white;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>

<body>
  <div id="app">
    <v-app>
      <v-main>
      <v-simple-table>
        <template v-slot:default>
        <thead>
          <tr>
            <th class="text-left">Car</th>
            <th class="text-left">Number</th>
            <th class="text-left">Quantity</th>
            <th class="text-left">Total</th>

          </tr>
        </thead>
        <tbody>
          <tr v-for="(car, index) in itemsWithTotal" :key="car.id" :class="car.type">
            <template v-if="car.type==='item'">
              <td>{{car.carro}}</td><td>{{car.number}}</td><td>{{car.quantity}}</td><td>{{car.total}}</td>
            </template>
            <template v-else>
            <td colspan="3">Subtotal</td><td>{{car.sum}}</td>
            </template>
          </tr>
        </tbody>
        </template>
</v-simple-table>
       
      </v-main>
    </v-app>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

If you do want to use v-data-table, I think you have two options:

  1. Use a grouping with a grouping summary slot and live with the awkwardness grouping introduces (i.e. grouping column is removed from table, changes to layout, weird "group" label in header):

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data: {
    headers:[
      { text: 'Car', value: 'carro'},
      { text: 'Number', value: 'number'},
      { text: 'Quantity', value: 'quantity'},
      { text: 'Total', value: 'total'},
    ],
    items: [
     {carro: '11016', number: '1', quantity: '4', total: '2'},
     {carro: '11016', number: '2', quantity: '3', total: '4'},
     {carro: '1122', number: '2', quantity: '4', total: '5'},
     {carro: '1122', number: '1', quantity: '1', total: '4'},
     {carro: '1133', number: '1', quantity: '2', total: '3'}, 
    ]
  },
  computed: {
    itemsWithId() {
      return this.items.map((item,i) => ({...item, id: i}))
    }
  }
  
})
.v-row-group__summary{
  background: grey!important;
  color: white;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>

<body>
  <div id="app">
    <v-app>
      <v-main>
        <v-data-table
          :headers=headers
          :items=itemsWithId
          item-key="id"
          sort-by="carro"
          group-by="carro"
          show-group-by
        >
          <template v-slot:group.summary="{items}">
            <td :colspan="headers.length-2">Subtotal</td><td>{{ items.reduce((s,i) => s+Number(i.total),0) }}</td>
          </template>
        </v-data-table>
       
      </v-main>
    </v-app>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
  1. Override the item slot and define how rows are rendered manually. I think you could pretty much put in the code from the simple table above. But then you have to make sure the table functionality (pagination, filtering, sorting) works in a reasonable way.

Does that make sense? Does one of the options work for you?

Moritz Ringler
  • 9,772
  • 9
  • 21
  • 34
  • hi @Moritz Ringler, your fist option should work. But i'was thinking about and the best solution is puting the subtotals inside the list, because i use the list to create an pdf. I edited the post, maybe you could help me. But if it is not possible, i'll use your fist option – Johny Freitas Mar 27 '23 at 17:05
  • Yes, have a look at the first snippet, the `itemsWithTotal` contains both entries and subtotals. In `v-data-table`, you'd have to override the item slot, using grouping does not work like that. – Moritz Ringler Mar 27 '23 at 17:25