停止或反向突出显示已单击的 Vuetify v-tab 项目

Stop or reverse highlighting of clicked Vuetify v-tab item

我有一个 VueJS/Vuetify 应用程序,它的标签栏使用 v-tabs/v-tab 组件在页面之间导航。我已经使用 v-tab 元素中的 click 事件实现了代码,该代码检查以确保当用户单击另一个选项卡时没有未保存的内容,如果有,则使用 [=17 显示模态=] 提醒用户。如果用户选择继续,它将继续到所需的 tab/component。但是,如果用户 selects Cancel 在模式中,页面将保留在原处。

这是选项卡组件:

<template>
  <div>
    <!-- Tabs -->
    <v-tabs
      color="secondary"
      :value="currentTab"
    >
      <v-tab
        v-for="(tab, i) in userTabs"
        :key="i"
        :href="tab.href"
        @click="tabClick(tab.component, tab.link)"
        :disabled="isDisabled(tab)"
      >
        {{ tab.title }}
      </v-tab>
    </v-tabs>
    <BaseConfirmModal
      :value="showUnsaved"
      :title="unsavedContentTitle"
      :text="unsavedContentText"
      declineText="Cancel"
      @clicked="unsavedModalConfirm"
    />
  </div>
</template>

<script>
import { mapGetters, mapActions } from 'vuex';
import baseTabMixin from '@/components/mixins/workspace/baseTabMixin';

export default {
  name: 'UserTabs',
  data: () => ({
    userTabs: [
      {
        title: 'General Info',
        href: '#tab-general',
        link: 'tab-general',
        component: 'UserEdit',
      },
      {
        title: 'Enrollments',
        href: '#tab-enrollments',
        link: 'tab-enrollments',
        component: 'UserEnrollmentEdit',
      },
      {
        title: 'Alerts',
        href: '#tab-alerts',
        link: 'tab-alerts',
        component: 'UserAlertEdit',
      },
      {
        title: 'Devices',
        href: '#tab-devices',
        link: 'tab-devices',
        component: 'UserDeviceEdit',
      },
    ],
  }),
  computed: {
    ...mapGetters('app', ['getStickyTenant', 'roleAtLeastTa', 'getUnsaved']),
    ...mapGetters('users', ['getCurrent']),
    ...mapGetters('tabs', {
      currentTab: 'getSelected',
    }),
  },
  methods: {
    ...mapActions('tabs', {
      setCurrentTab: 'setSelected',
    }),
    isDisabled(item) {
      if (item.component === 'UserEdit') {
        return false;
      }
      if (item.component === 'UserDeviceEdit' && !this.roleAtLeastTa) {
        return true;
      }

      return !this.getCurrent.userId;
    },
  },
  mixins: [baseTabMixin],
};
</script>

和引用的 baseTabMixin:

import { mapGetters, mapActions } from 'vuex';

const baseTabMixin = {
  data: () => ({
    showUnsaved: false,
    unsavedContentTitle: 'Unsaved Changes',
    unsavedContentText: 'You have made changes to this page that are not saved. Do you wish to continue?',
    destTabComponent: '',
    destTabLink: '',
  }),
  components: {
    BaseConfirmModal: () => import('@/components/base/BaseConfirmModal'),
  },
  computed: {
    ...mapGetters('app', ['getUnsaved']),
  },
  methods: {
    ...mapActions('app', ['setUnsaved']),
    tabClick(component, tab) {
      // Check to see if getUnsaved === true; if it is,
      // set variable to display warning modal.
      if (this.getUnsaved) {
        this.showUnsaved = true;
      } else {
        // There is no unsaved content, so continue to the desired tab.
        this.destTabComponent = component;
        this.destTabLink = tab;
        this.setCurrentTab(tab);
        this.$router.push({ name: component });
      }
    },
    unsavedModalConfirm(confirm) {
      if (confirm) {
        this.setCurrentTab(this.destTabLink);
        this.$router.push({ name: this.destTabComponent });
      }
      this.showUnsaved = false;
    },
  },
};

export default baseTabMixin;

问题与选项卡项目突出显示有关。单击新选项卡时,滑块将移动到新选项卡,并且在调用单击事件(在本例中为 tabClick())之前,新选项卡标题会加粗。当我在我的模式中 select Cancel 时,它会将页面留在原处(如预期的那样),但单击的选项卡仍然突出显示,下方的滑块和更粗的文本。由于这一切都发生在我的点击处理程序被调用之前,有没有办法 a) 在调用点击事件之前停止突出显示,或者 b) 将突出显示反转回当前选项卡?

Link 工作 pen.

注意代码的关键部分:-

<v-tabs :value="currentTab" @change="onTabChange">
      <v-tab v-for="(tab, i) in tabs" :key="i">{{tab.title}}</v-tab>
</v-tabs>
async onTabChange(clickedTab)
{
    this.currentTab = clickedTab;
    await this.$nextTick();
    if (!this.allowTabChange) this.currentTab = this.previousTab;
    else this.previousTab = this.currentTab;
}

v-tabs 组件使用内部模型来维护其状态,即哪个选项卡当前处于活动状态。使用 :value 属性,我们可以设置一个初始活动选项卡。当用户单击不同的选项卡时,内部模型会更新,导致单击的选项卡突出显示。 v-tabs 还会发出一个更改事件来通知父组件此更改。我们需要监听此事件并自行管理 :value 变量以维护父级中的状态。由于 v-model:value@change 的抽象,我们也可以使用它。

要侦听此更改事件,请将 @change 添加到 v-tabs。您可以将 @click 中的整个逻辑转移到 @change 中,从而使代码更清晰。

为了防止选择下一个选项卡,我们需要使用一些棘手的代码,因为我们无法访问 v-tabs 的内部模型。我们需要允许 currentTab 临时更改为单击的选项卡,然后在 $nextTick 中将其重置为 previousTab,即在 Vue 获取当前批次更新之后。选项卡下方的突出显示出现在 $nextTick 之后,因此它不会更改,因为届时我们已将 :value 重置为 previousTab

如果您可以让突出显示暂时转移到新标签,那么您需要做的就是更新 currentTab 到下一个标签的索引,突出显示将照常转移。然后根据用户的决定,您可以将 currentTab 设置为 previousTab 以恢复更改,或者您可以使用 currentTab 的新值更新 previousTab 即下一个选项卡.在这种情况下,$nextTick hack 是不必要的。

多亏了@ParaBolt 和他的 CodePen 的回答,我才开始工作。我的回答和他的略有不同,因为我的 currentTab 存储在状态中,而不是组件中,我也使用了 filter 方法来获取 component 值,但结构是相同的。

以下是 mixin 的重要部分:

 data: () => ({
    ..
    previousTab: '',
  }),
  methods: {
    ...mapActions('app', ['setUnsaved']),
    async tabChange(newTab) {
      this.setCurrentTab(newTab);
      // We need to get the corresponding component value from the tabs array.
      const newComponent = this.tabList.filter(tab => tab.link === newTab)[0].component;
      // Stash these in case we need then in unsavedModalConfirm()
      this.destTabComponent = newComponent;
      this.destTabLink = newTab;
      // Check to see if getUnsaved === true; if it is,
      // set variable to display warning modal.
      await this.$nextTick();
      if (this.getUnsaved) {
        this.setCurrentTab(this.previousTab);
        this.showUnsaved = true;
      } else {
        // There is no unsaved content, so continue to the desired tab.
        this.setCurrentTab(newTab);
        this.$router.push({ name: newComponent });
      }
    },
    unsavedModalConfirm(confirm) {
      if (confirm) {
        // User selected Continue from modal.
        this.setCurrentTab(this.destTabLink);
        this.$router.push({ name: this.destTabComponent });
        this.setUnsaved(false);
      } else {
        // User selected Cancel
        this.setCurrentTab(this.previousTab);
      }
      this.showUnsaved = false;
    },
  },
  mounted() {
    this.previousTab = this.currentTab;
  },

每当我从模式中取消时,它只会停留在原处,如果我选择继续,它会转到所需的选项卡。