首页 > 解决方案 > 尝试在 VueJs 中禁用导航按钮时出错

问题描述

在我的工单处理应用程序中,我目前在 TicketRunner.vue 组件中包含一个后退和前进按钮,我想更改它,以便这些按钮仅在我有关联的案例文件时出现,为此我使用了 V-If :

TicketRunner.Vue

<div class="level nav-btns" v-if='!currentTicketCaseFiles.length'>
    <div class="buttons has-addons level-left">
      <b-button
        @click.prevent="navGoPrev()"
        :disabled="currentStepIndex === 0 || navWaiting"
        size="is-medium"
      >
    </div>


   export default {
  name: 'TicketRunner',

  mixins: [NavStepsByIndexMixin()],

  components: {
    StagePresenter,
    CaseFilesStage,
    ParticipantsStage,
    AttachmentsStage,
    CaseFilesRunner,
    TicketContextButtons,
  },

  data: function() {
    return {
      firstComponentsInitialization: true,
      loadingConfirm: false,
      confirmationModalActive: false,
      confirmationSucceeded: undefined
    }
  },

  props: {
    ticketId: {
      type: Number,
      required: true,
    },
  },

  provide() {
    return  {
      contextButtons: {
        capture: (name, callback, title) => this.$refs['contextButtons'].captureButton(name, callback, title),
        release: (name) => this.$refs['contextButtons'].releaseButton(name),
        enable: (name) => this.$refs['contextButtons'].enableButton(name),
        disable: (name) => this.$refs['contextButtons'].disableButton(name),
      },
    };
  },

  computed: {
    ...mapGetters(['currentTicket', 'ticketCaseFiles', 'allCurrentTicketAttachments', 'currentTicketCaseFileNotAssociated',
      'currentRequesterType', 'currentTicketStage', 'lastCaseFile']),
    caseFiles() {
      return this.ticketCaseFiles(this.ticketId);
    },
    ticketHasAttachments() {
      return this.allCurrentTicketAttachments.length > 0;
    },
    isTicketAssociatedWithCaseFile() {
      return !this.currentTicketCaseFileNotAssociated;
    },
    isFirstNavInitializationInProgress() {
      return !this.navReady && this.firstComponentsInitialization;
    },
    isShowAttachmentsStep() {
      return this.ticketHasAttachments && this.currentRequesterType !== 'unknown' &&
               (this.isFirstNavInitializationInProgress || this.isTicketAssociatedWithCaseFile)
    },
    isCurrentTicketResolved() {
      return this.currentTicket.status === 'resolved';
    },
    islastStep() {
      return this.navLastStep() && this.lastCaseFile; 
    }
  },

  watch: {
    ticketId(){
      this.navigator.reset();
    },
    navReady() {
      this.moveForwardIfReady();
      this.firstComponentsInitialization = false;
    }
  },

  methods: {
    ...mapActions(['confirmTicket']),

    moveForwardIfReady() {
      if (this.navigator.currentIndex === 0 && this.firstComponentsInitialization) {
        let steps = 0
        const step_names = ['case_files_stage']
        for(const [_idx, name] of step_names.entries()) {
          const ref_name = `step[${name}]`;
          if (this.$refs.hasOwnProperty(ref_name) && this.$refs[ref_name].navReady) {
            steps += 1
          } else {
            break
          }
        }
        this.navigator.currentIndex += steps
      }
    },

    confirm() {
      this.$buefy.dialog.confirm({
        message: this.t('tickets.stages.confirmation.simplified_confirm_reply'),
        onConfirm: () => this.confirmStep()
      })
    },

    async confirmStep() {
      this.loadingConfirm = true;
      const promise = this.confirmTicket(this.ticketId);

      return promise.then((response) => {
        this.confirmationModalActive = true;
        this.confirmationSucceeded = true;
        return true; // true is correct here. for goNext it makes parent to stay on on the current step
      }).catch(() => {
        this.confirmationModalActive = true;
        this.confirmationSucceeded = false;
        return true; // true is correct here. for goNext it makes parent to stay on on the current step
      }).finally(() => this.loadingConfirm  = false);
    },
  },
};

然后我收到以下控制台错误:

[Vue warn]: Property or method "currentTicketCaseFiles" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.

我知道“!currentTicketCaseFiles.length”在组件CaseFilesStage.vue 中成功运行,这让我相信我应该以某种方式将两者联系起来?但是导入它对我来说似乎也不合适。我不太确定如何解决这个问题,因为我是 VueJS 的新手,并且会很高兴任何指针。我将在下面附上 CaseFilesStage.vue 组件。

CaseFilesStage.vue

<template>
  <div class="hero">
    <div class="block">
      <template v-if="!currentTicket.spamTicket">
        <b-field>
          <b-input
              v-model="filter"
              :loading="loading"
              :placeholder="t('tickets.stages.case_files.search.tooltip')"
              v-on:keyup.enter.native="searchCaseFiles"
              type="search"
              icon="search"
              :class="{ 'preview-enabled': showAttachmentsPreview}"
          />
        </b-field>
        <template v-if="foundCaseFiles.length">
          <h4 class="title is-4 table-title">{{ t('tickets.stages.case_files.search.table_title') }}</h4>
          <CaseFilesSearchTable
              :case-files="foundCaseFilxes"
              :found-by-data-points="foundCaseFilesByParticipant"
              :show-header="true"
              v-slot="cf">
            <b-checkbox v-if="cfBelongsToCurrentTicket(cf.id)" :disabled="true" :value="true"></b-checkbox>
            <b-checkbox v-else @input="onFoundCaseFile(cf.id, $event)"></b-checkbox>
          </CaseFilesSearchTable>
        </template>
        <div v-else-if="lookupStatus === 'notFound'">
          {{ t('tickets.stages.case_files.search.not_found') }}
          <!-- display button here if above is activated -->
        </div>
      </template>
    </div>
    <template v-if='currentTicketCaseFiles.length'>
      <h4 class="title is-4 table-title">{{ t('tickets.stages.case_files.table_title') }}</h4>
      <CaseFilesTable :case-files="currentTicketCaseFiles" :show-header="true" v-slot="cf">
        <DeleteButton
          :model-id="cf.id"
          modelName="CaseFile" >
        </DeleteButton>
      </CaseFilesTable>
    </template>
  </div>
</template>

<script>

import CaseFilesTable from '../tables/CaseFilesTable';
import CaseFilesSearchTable from '../tables/CaseFilesSearchTable';
import DeleteButton from '../../../../shared/components/controls/DeleteButton';

import { mapGetters, mapActions } from 'vuex';
import { mapServerActions } from "../../../../../../_frontend_infrastructure/javascript/lib/crudvuex_new";

export default {
  name: 'CaseFilesStage',

  data() {
    return {
      lookupStatus: 'waitingInput',
      filter: '',
      waiting: {},
      foundCaseFiles: [],
      foundCaseFilesByParticipant: {}
    };
  },

  components: {
    CaseFilesTable,
    CaseFilesSearchTable,
    DeleteButton
  },

  computed: {
    ...mapGetters(
      ['currentTicketCaseFiles', 'currentTicketCaseFileNotAssociated', 'currentTicket', 'showAttachmentsPreview']
    ),
    loading() {
      return this.lookupStatus === 'waitingServer';
    },

    allCaseFilesMix(){
      this.currentTicketCaseFiles + this.foundCaseFiles
    },

    foundCaseFilesEx() {
      return this.foundCaseFiles.filter((x) => !this.cfBelongsToCurrentTicket(x.id))
    },

    checkboxValue() {
      if(!this.currentTicketCaseFileNotAssociated) {
        return null;
      }

      return true;
    },

    navReady() {
      return this.currentTicket.spamTicket || this.currentTicketCaseFiles.length > 0 || this.checkboxValue;
    },
    markSpam: {
      get: function() {
        return this.currentTicket.spamTicket
      },
      set: function(val) {
        return this.updateTicket([this.currentTicket.id, { spam_ticket: val }]);
      },
    }

  },

  methods: {
    ...mapActions(['updateTicket']),
    ...mapServerActions(['createCaseFile', 'deleteCaseFile']),

    cfBelongsToCurrentTicket(id){
      return this.currentTicketCaseFiles.map((x) => x.caseFileId).includes(id);
    },

    cantAssignCaseFileCheckbox(isChecked){
      if(isChecked) {
         this.createCaseFile({ isCfNotAssociated: true });
      } else {
         this.deleteCaseFile(this.currentTicketCaseFileNotAssociated);
      }
    },

    onFoundCaseFile(id, useIt){
      console.log("onFoundCaseFile: ", id, useIt);

      if(useIt) {
         this.createCaseFile({ caseFileId: id });
      } else {
         this.deleteCaseFile(this.currentTicketCaseFiles.find({ caseFileId: id }));
      }
    },

    searchCaseFiles() {
      const newData = this.filter;

      if (newData.length < 3) { // TODO: some smarter condition here
        this.foundCaseFiles = [];
        this.lookupStatus = 'waitingInput';
        return;
      }

      this.lookupStatus = 'waitingServer';
      this.$axios.get('case_files', { params: { "case_files.filter" : newData } })
        .then((response) => {
          this.foundCaseFiles = response.data.caseFilesSearchResult.caseFiles;
          this.foundCaseFilesByParticipant = response.data.caseFilesSearchResult.foundByPrivateInfo;
          if(this.foundCaseFiles.length > 0) {
            this.lookupStatus = 'success';
          } else {
            this.lookupStatus = 'notFound';
          }
        }).catch(() => this.lookupStatus = 'error');
    }
  },
};
</script>

</style>

标签: vue.jsvuejs2vue-component

解决方案


将此添加到您的TicketRunner.vue组件脚本中:

computed: {
  ...mapGetters(['currentTicketCaseFiles'])
}

推荐阅读