【问题标题】:Nuxt + Vue 3: Make all table columns sortable and isolate sort click action to just change icons on sorted columnNuxt + Vue 3:使所有表格列可排序并隔离排序单击操作以仅更改已排序列上的图标
【发布时间】:2021-02-12 16:05:50
【问题描述】:

我有一个表,其中所有列都是可排序的,排序功能工作正常,但我在试图弄清楚如何让我的图标仅针对我选择排序的列改变方向时遇到了很多麻烦。使用我的方法,每次单击一个图标时,所有其他图标也会改变方向,我知道为什么会这样(我只是根据 currentSortDir 状态属性(asc 或 desc)评估显示或不显示图标,我也知道我必须给每一列起某种名称来评估我的状态属性 currentSort(它将具有当前排序列的名称),但我不知道如何使用 v-if 之类的东西来使它工作,也许我必须更改我的模板结构?我没有使用多类方法来更改排序图标,而是使用两个 svg,它们将根据排序方法有条件地显示。

模板:

<template>
  <div>
    <h1 class="text-center font-semibold">Agents</h1>
    <div class="px-4 mt-6">
      <table class="text-left w-full">
        <thead>
          <tr>
            <th class="px-4 py-2" @click="sort('email')">
              <div
                class="bg-gray-300 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
              >
                <span>
                  <IconBase
                    v-if="currentSortDir === 'asc'"
                    class="fill-current w-4 h-4 mr-2"
                    icon-color="rgba(0,0,0,0.80)"
                    icon-name="asc"
                    ><IconAsc
                  /></IconBase>
                  <IconBase
                    v-if="currentSortDir === 'desc'"
                    class="fill-current w-4 h-4 mr-2"
                    icon-color="rgba(0,0,0,0.80)"
                    icon-name="desc"
                    ><IconDesc
                  /></IconBase>
                </span>
                <span>Name</span>
              </div>
            </th>
            <th class="px-4 py-2" @click="sort('email')">
              <div
                class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
              >
                <span>
                  <IconBase
                    v-if="currentSortDir === 'asc'"
                    class="fill-current w-4 h-4 mr-2"
                    icon-color="rgba(0,0,0,0.80)"
                    icon-name="asc"
                    ><IconAsc
                  /></IconBase>
                  <IconBase
                    v-if="currentSortDir === 'desc'"
                    class="fill-current w-4 h-4 mr-2"
                    icon-color="rgba(0,0,0,0.80)"
                    icon-name="desc"
                    ><IconDesc
                  /></IconBase>
                </span>
                <span>Email</span>
              </div>
            </th>
            <th class="px-4 py-2" @click="sort('wsConnectionStatus')">
              <div
                class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
              >
                <span>
                  <IconBase
                    v-if="currentSortDir === 'asc'"
                    class="fill-current w-4 h-4 mr-2"
                    icon-color="rgba(0,0,0,0.80)"
                    icon-name="checkmark"
                    ><IconAsc
                  /></IconBase>
                  <IconBase
                    v-if="currentSortDir === 'desc'"
                    class="fill-current w-4 h-4 mr-2"
                    icon-color="rgba(0,0,0,0.80)"
                    icon-name="checkmark"
                    ><IconDesc
                  /></IconBase>
                </span>
                <span>WS</span>
              </div>
            </th>
            <th class="px-4 py-2" @click="sort('status')">
              <div
                class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
              >
                <IconBase
                  v-if="currentSortDir === 'asc'"
                  class="fill-current w-4 h-4 mr-2"
                  icon-color="rgba(0,0,0,0.80)"
                  icon-name="checkmark"
                  ><IconAsc
                /></IconBase>
                <IconBase
                  v-if="currentSortDir === 'desc'"
                  class="fill-current w-4 h-4 mr-2"
                  icon-color="rgba(0,0,0,0.80)"
                  icon-name="checkmark"
                  ><IconDesc
                /></IconBase>
                <span>Status</span>
              </div>
            </th>
            <th class="px-4 py-2" @click="sort('priorityRank')">
              <button
                class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
              >
                <IconBase
                  v-if="currentSortDir === 'asc'"
                  class="fill-current w-4 h-4 mr-2"
                  icon-color="rgba(0,0,0,0.80)"
                  icon-name="checkmark"
                  ><IconAsc
                /></IconBase>
                <IconBase
                  v-if="currentSortDir === 'desc'"
                  class="fill-current w-4 h-4 mr-2"
                  icon-color="rgba(0,0,0,0.80)"
                  icon-name="checkmark"
                  ><IconDesc
                /></IconBase>
                <span>Rank</span>
              </button>
            </th>
            <th class="px-4 py-2" @click="sort('numberChats')">
              <button
                class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
              >
                <IconBase
                  v-if="currentSortDir === 'asc'"
                  class="fill-current w-4 h-4 mr-2"
                  icon-color="rgba(0,0,0,0.80)"
                  icon-name="checkmark"
                  ><IconAsc
                /></IconBase>
                <IconBase
                  v-if="currentSortDir === 'desc'"
                  class="fill-current w-4 h-4 mr-2"
                  icon-color="rgba(0,0,0,0.80)"
                  icon-name="checkmark"
                  ><IconDesc
                /></IconBase>
                <span>Number Chats</span>
              </button>
            </th>
            <th class="px-4 py-2" @click="sort('currentMaxChats')">
              <button
                class="bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
              >
                <IconBase
                  v-if="currentSortDir === 'asc'"
                  class="fill-current w-4 h-4 mr-2"
                  icon-color="rgba(0,0,0,0.80)"
                  icon-name="checkmark"
                  ><IconAsc
                /></IconBase>
                <IconBase
                  v-if="currentSortDir === 'desc'"
                  class="fill-current w-4 h-4 mr-2"
                  icon-color="rgba(0,0,0,0.80)"
                  icon-name="checkmark"
                  ><IconDesc
                /></IconBase>
                <span>Max Current</span>
              </button>
            </th>
          </tr>
        </thead>
        <tbody>
          <Agent
            v-for="(agent, index) in sortedAgents"
            :key="index"
            :agent="agent"
            :data-index="index"
          ></Agent>
        </tbody>
      </table>
    </div>
  </div>
</template>

状态:

<script>
import { mapState } from 'vuex'
import IconBase from '@/components/Icons/IconBase.vue'
import IconAsc from '@/components/Icons/IconAsc.vue'
import IconDesc from '@/components/Icons/IconDesc.vue'
import Agent from '@/components/Agent.vue'
export default {
  components: {
    Agent,
    IconBase,
    IconAsc,
    IconDesc,
  },
  async fetch({ store, error }) {
    try {
      await store.dispatch('agents/fetchAgents')
    } catch (e) {
      error({
        statusCode: 503,
        message: 'Unable to fetch events at this time. Please try again later',
      })
    }
  },
  data() {
    return {
      currentSort: 'email',
      currentSortDir: 'asc',
    }
  },
  computed: {
    ...mapState({
      agents: (state) => state.agents.agents,
    }),
    sortedAgents() {
      return this.agents.slice().sort((a, b) => {
        let modifier = 1
        if (this.currentSortDir === 'desc') modifier = -1
        if (a[this.currentSort] < b[this.currentSort]) return -1 * modifier
        if (a[this.currentSort] > b[this.currentSort]) return 1 * modifier
        return 0
      })
    },
  },
  methods: {
    sort(columnToSort) {
      if (columnToSort === this.currentSort) {
        this.currentSortDir = this.currentSortDir === 'asc' ? 'desc' : 'asc'
      }
      this.currentSort = columnToSort
    },
  },
}
</script>

【问题讨论】:

    标签: javascript html vue.js nuxt.js


    【解决方案1】:

    假设一次只能升序一列,只需将 currentSort 添加到条件。

    
    <IconBase
      v-if="currentSortDir === 'asc' && currentSort === 'email'"
      class="fill-current w-4 h-4 mr-2"
      icon-color="rgba(0,0,0,0.80)"
      icon-name="checkmark"
      >
      <IconAsc/>
    </IconBase>
    <IconBase
      v-else
      class="fill-current w-4 h-4 mr-2"
      icon-color="rgba(0,0,0,0.80)"
      icon-name="checkmark"
      >
      <IconDesc/>
    </IconBase>
    

    注意 desc 图标中的 v-else

    关于双击问题,试试这个:

    if (columnToSort === this.currentSort) {
      this.currentSortDir = this.currentSortDir === 'asc' ? 'desc' : 'asc'
    } else {
      this.currentSortDir = 'asc'
    }
    this.currentSort = columnToSort
    

    【讨论】:

    • 非常感谢@NickDawes,这是成功的秘诀。只是一件事,我注意到排序号召性用语( 标签)不会在第一次点击时做出反应,而是在第二次点击时做出反应。你知道那里发生了什么吗?
    • 另外:您对排序方法的建议不起作用。 !this.currentSortdir 使 currentSortdir = false 并破坏代码,因为我想要该状态属性的值“asc”或“desc”。
    • 看起来你在方法中有一个条件,检查currentSort 是否等于你第一次点击 TH 时传入的字符串。第一次点击时这是错误的。第二次单击时,currentSort 已更新为您通过第一次单击传入的字符串,因此条件为真,然后currentSortDir 才会更新。
    • 你是完全正确的,我关于布尔开关的错误!我已经更新了我的答案。
    • 我已经更新了我的答案,提出了解决双击问题的建议。
    猜你喜欢
    相关资源
    最近更新 更多
    热门标签