3

我是 vue.js 的新手,请帮忙。我的表中需要过滤器。每列必须有多个按值过滤的选择。当我们单击列标题时 - 下拉多选打开,我们可以选择过滤值。例如像这个Vuetify 数据表内联过滤器,但这个例子不适用于 vuetify 2。 在此处输入图像描述

我的html:

<template>
 <v-card class="elevation-3">
         <v-card-title>
           {{ other_title }}
           <v-btn style="background-color: white; box-shadow: none;" @click="csvExport(other_title, otherIncidentsData)">
               CSV<i class="fas fa-file-csv"></i>
           </v-btn>
           <v-btn text @click="exportToPdf()">
              Pdf<v-icon>mdi-file-pdf-box-outline</v-icon>
            </v-btn>
            <v-btn text @click="showExportModal">
              <v-icon>mdi-email-receive-outline</v-icon>
            </v-btn>
           <v-spacer></v-spacer>
           <v-text-field
             v-model="search"
             append-icon="mdi-magnify"
             label="Search"
             single-line
             hide-details
           ></v-text-field>
         </v-card-title>
         <v-data-table
           :search="search"
           :headers="headers"
           :items="otherIncidents"
           :items-per-page="10"
         >
          <template v-slot:item.priority.name="{ item }">
              <v-chip :color="getPriorityColor(item.priority.name)" dark>{{ item.priority.name }}</v-chip>
          </template>
         </v-data-table>
        </v-card>
</template>

和 JS:

 export default {
      mixins: [
        mixin
      ],
      data() {
        return {
          search: '',
          title: 'MediaMyne reports',
          project_title: 'PROJECTS (open at the end of the reporting period)',
          new_title: 'NEW REQUESTS (created during the reporting period)',
          other_title: 'OTHER REQUESTS (remaining open or changed during the reporting period)',
          tabs: [
            { name: 'Projects' },
            { name: 'New Requests' },
            { name: 'Other Requests' },
          ],
          headers: [
            {
              text: 'Company', align: 'start', sortable: true, value: 'customer.name', width: '14%',
            },
            {
              text: 'Name (Costumer Contact)', align: 'start', sortable: true, value: 'reported_By_Customer_Contact.name', width: '16%',
            },
            {
              text: 'Title', align: 'start', sortable: true, value: 'name', width: '17%',
            },
            {
              text: 'Days open', align: 'center', sortable: true, value: 'daysOpen', width: '9%',
            },
            {
              text: 'Days waiting', align: 'center', sortable: true, value: 'daysWaiting', width: '10%',
            },
            {
              text: 'Workflow step', align: 'start', sortable: true, value: 'workflow_Step.name', width: '12%',
            },
            {
              text: 'Support type', align: 'start', sortable: true, value: 'custom_Fields.customFields.custom_266', width: '12%',
            },
            {
              text: 'Priority', align: 'start', sortable: true, value: 'priority.name', width: '10%',
            },
          ],
          otherIncidents: [],
          newIncidents: [],
          projectIncidents: [],
          activeTab: 0,
          pdfReportTitle: ''
        };
      },
      components: {
        ExportModal
      },
      computed: {
      ...mapGetters([
        'isLoggedIn'
      ]),
      otherIncidentsData() {
          return this.otherIncidents.map(item => ({
            Company: item.customer.name,
            Costumer_contact_name: item.reported_By_Customer_Contact.name,
            Title: item.name,
            Days_open: item.daysOpen,
            Days_waiting: item.daysWaiting,
            Workflow_step: item.workflow_Step.name,
            Support_type: item.custom_Fields.customFields.custom_266,
            Priority: item.priority.name
          }));
        },
      methods: {
        ...mapActions([
          'setLoginState'
        ]),
        getPriorityColor(priority) {
          switch (priority.toLowerCase()) {
            case 'critical':
              return '#fc0000';
            case 'high':
              return '#c20202';
            case 'normal':
              return '#dd7417';
            case 'low':
              return '#318d14';
            default:
              return 'rgb(0,0,0,0)';
          }
         }
    };
4

1 回答 1

4

这些天我在codepen上做这个,因为我没有找到任何好的例子:

https://codepen.io/manuel-84/pen/NWxLLmN(正在进行中)

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data: () => ({
    dialog: false,
    filters: { 'name': [], 'calories': [], 'status': [] },
    activeFilters: {},
    desserts: [],
    editedIndex: -1,
    editedItem: {
      name: '',
      calories: 0,
      fat: 0,
      carbs: 0,
      protein: 0,
    },
    defaultItem: {
      name: '',
      calories: 0,
      fat: 0,
      carbs: 0,
      protein: 0,
    },
  }),

  computed: {
    headers () {
      return [
        {
          text: 'Dessert (100g serving)',
          align: 'start',
          sortable: true,
          value: 'name',
          filter: value => {
            return this.activeFilters.name.includes(value);
          }
        },
        { text: 'Calories', value: 'calories',
          filter: value => {
            return this.activeFilters.calories.includes(value);
          }
        },
        { text: 'Status', value: 'status',
          filter: value => {
            return this.activeFilters.status.includes(value);
          }
        },
        { text: 'Fat (g)', value: 'fat' },
        { text: 'Carbs (g)', value: 'carbs' },
        { text: 'Protein (g)', value: 'protein' },
        { text: 'Actions', value: 'actions', sortable: false },
      ]
    },
    formTitle () {
      return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
    },
  },

  watch: {
    dialog (val) {
      val || this.close()
    },
    /*filters: {
      deep: true,
      handler(val) {
        console.log(val)
      }
    }*/
  },

  created () {
    this.initialize()
  },

  methods: {
    initialize () {
      this.desserts = [
        {
          name: 'Frozen Yogurt',
          calories: 159,
          fat: 6.0,
          carbs: 24,
          protein: 4.0,
          status: 'DIET'
        },
        {
          name: 'Ice cream sandwich',
          calories: 237,
          fat: 9.0,
          carbs: 37,
          protein: 4.3,
          status: 'NO DIET'
        },
        {
          name: 'Eclair',
          calories: 262,
          fat: 16.0,
          carbs: 23,
          protein: 6.0,
          status: 'DIET'
        },
        {
          name: 'Cupcake',
          calories: 305,
          fat: 3.7,
          carbs: 67,
          protein: 4.3,
          status: 'NO DIET'
        },
        {
          name: 'Gingerbread',
          calories: 356,
          fat: 16.0,
          carbs: 49,
          protein: 3.9,
          status: 'DIET'
        },
        {
          name: 'Jelly bean',
          calories: 375,
          fat: 0.0,
          carbs: 94,
          protein: 0.0,
          status: 'NO DIET'
        },
        {
          name: 'Lollipop',
          calories: 392,
          fat: 0.2,
          carbs: 98,
          protein: 0,
          status: 'NO DIET'
        },
        {
          name: 'Honeycomb',
          calories: 408,
          fat: 3.2,
          carbs: 87,
          protein: 6.5,
          status: 'NO DIET'
        },
        {
          name: 'Donut',
          calories: 452,
          fat: 25.0,
          carbs: 51,
          protein: 4.9,
          status: 'DIET'
        },
        {
          name: 'KitKat',
          calories: 518,
          fat: 26.0,
          carbs: 65,
          protein: 7,
          status: 'NO DIET'
        },
      ];
      for (col in this.filters) {
        this.filters[col] = this.desserts.map((d) => { return d[col] }).filter(
          (value, index, self) => { return self.indexOf(value) === index }
        );
      }
      this.activeFilters = Object.assign({}, this.filters)
    },
    
    toggleAll (col) {
      this.activeFilters[col] = this.desserts.map((d) => { return d[col] }).filter(
        (value, index, self) => { return self.indexOf(value) === index }
      )
    },
    
    clearAll (col) {
      this.activeFilters[col] = []
    },

    editItem (item) {
      this.editedIndex = this.desserts.indexOf(item)
      this.editedItem = Object.assign({}, item)
      this.dialog = true
    },

    deleteItem (item) {
      const index = this.desserts.indexOf(item)
      confirm('Are you sure you want to delete this item?') && this.desserts.splice(index, 1)
    },

    close () {
      this.dialog = false
      this.$nextTick(() => {
        this.editedItem = Object.assign({}, this.defaultItem)
        this.editedIndex = -1
      })
    },

    save () {
      if (this.editedIndex > -1) {
        Object.assign(this.desserts[this.editedIndex], this.editedItem)
      } else {
        this.desserts.push(this.editedItem)
      }
      this.close()
    },
    
  },
  
})
.v-list--dense .v-list-item, .v-list-item--dense {
  min-height: 20px !important;
  height: 2rem;
}

.v-application--is-ltr .v-list-item__action:first-child, .v-application--is-ltr .v-list-item__icon:first-child {
  margin-right: .5rem !important;
}
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.3.4/dist/vuetify.min.js"></script>

<link href="https://cdn.jsdelivr.net/npm/vuetify@2.3.4/dist/vuetify.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet"/>

<span id="app">
  <v-app id="inspire">
    <v-data-table :headers="headers" :items="desserts" sort-by="calories" item-key="name" show-select>
      <template v-for="(col, i) in filters" v-slot:[`header.${i}`]="{ header }">
        <div style="display: inline-block; padding: 16px 0;">{{ header.text }}</div>
        <div style="float: right; margin-top: 8px">
          <v-menu :close-on-content-click="false" :nudge-width="200" offset-y transition="slide-y-transition" left fixed style="position: absolute; right: 0">
            <template v-slot:activator="{ on, attrs }">
              <v-btn color="indigo" icon v-bind="attrs" v-on="on">
                <v-icon small 
                  :color="activeFilters[header.value].length < filters[header.value].length ? 'red' : 'default'">
                  mdi-filter-variant
                </v-icon>
              </v-btn>
            </template>
            <v-list flat dense class="pa-0">
              <v-list-item-group multiple v-model="activeFilters[header.value]" class="py-2">
                <template v-for="(item, i) in filters[header.value]">
                  <v-list-item :key="`item-${i}`" :value="item" :ripple="false">
                    <template v-slot:default="{ active, toggle }">
                      <v-list-item-action>
                        <v-checkbox :input-value="active" :true-value="item"
                          @click="toggle" color="primary" dense></v-checkbox>
                      </v-list-item-action>
                      <v-list-item-content>
                        <v-list-item-title v-text="item"></v-list-item-title>
                      </v-list-item-content>
                    </template>
                  </v-list-item>
                </template>
              </v-list-item-group>
              <v-divider></v-divider>
              <v-btn text block @click="toggleAll(header.value)">Toggle all</v-btn>
              <v-btn text block @click="clearAll(header.value)">Clear all</v-btn>
            </v-list>
          </v-menu>
        </div>
      </template>
      <template v-slot:header="{ props: { headers } }">
        <thead>
          <tr>
            <th :colspan="headers.length">
              This is a header
            </th>
          </tr>
        </thead>
      </template>
      <template v-slot:top>
        <v-toolbar flat color="white">
          <v-toolbar-title>My CRUD</v-toolbar-title>
          <v-divider class="mx-4" inset vertical></v-divider>
          <v-spacer></v-spacer>
          <v-dialog v-model="dialog" max-width="500px">
            <template v-slot:activator="{ on, attrs }">
              <v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">New Item</v-btn>
            </template>
            <v-card>
              <v-card-title>
                <span class="headline">{{ formTitle }}</span>
              </v-card-title>
              <v-card-text>
                <v-container>
                  <v-row>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.name" label="Dessert name"></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.calories" label="Calories"></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.fat" label="Fat (g)"></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.carbs" label="Carbs (g)"></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.protein" label="Protein (g)"></v-text-field>
                    </v-col>
                  </v-row>
                </v-container>
              </v-card-text>
              <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn color="blue darken-1" text @click="close">Cancel</v-btn>
                <v-btn color="blue darken-1" text @click="save">Save</v-btn>
              </v-card-actions>
            </v-card>
          </v-dialog>
        </v-toolbar>
      </template>
      <template v-slot:item.actions="{ item }">
        <v-icon small class="mr-2" @click="editItem(item)">
          mdi-pencil
        </v-icon>
        <v-icon small @click="deleteItem(item)">
          mdi-delete
        </v-icon>
      </template>
      <template v-slot:no-data>
        <v-btn color="primary" @click="initialize">Reset</v-btn>
      </template>
    </v-data-table>
  </v-app>
  </div>

我希望代码清楚,无论如何随时问

于 2020-07-16T14:03:36.373 回答