Vue.js:在请求进行中时,将加载条添加到整个页面

huangapple go评论58阅读模式
英文:

Vue.js : Add loading bar to whole page when request is in progress

问题

在我的页面上,我有一个“编辑”按钮,当我点击它时,我可以编辑一些内容,然后保存或关闭而不保存。

我想在用户点击“保存”按钮之前添加一个加载进度条(就像页面正在加载),然后显示结果。

我现在拥有的是:

用户点击“编辑”按钮,一些数据变得可编辑,他会看到一个“保存”按钮和一个“关闭”按钮(用于不保存),如果他点击“保存”按钮,页面将重新加载以显示已编辑的数据,并显示通知消息“已更新”(如果更新失败,则显示错误消息)。

我想要的是:

当用户在进行编辑后点击“保存”按钮时,页面将显示一个加载进度条(以显示正在加载数据),持续几秒钟(例如,假设为5秒钟)。如果在这段时间内完成请求,加载进度条将结束,但如果请求尚未完成,加载进度条将持续加载,直到完成为止,然后再显示所有数据和“已更新”通知。

这是与此相关的一些代码:

我的按钮:

<v-col cols="3">
  <v-btn
    v-if="editGroup"
    light
    class="mx-2"
    @click="editGroup = false"
  >
    <v-icon left>mdi-close</v-icon>
    关闭
  </v-btn>
  <v-btn
    v-if="editGroup" 
    light 
    class="mx-2" 
    @click="clickSave"
  >
    <v-icon left>mdi-content-save</v-icon>
    保存
  </v-btn>
  <v-btn
    v-if="
      canShowButton(['administrateur', 'configurateur']) &&
      !editGroup
    "
    light
    class="mx-2"
    @click="editGroup = true"
  >
    <v-icon left>mdi-pencil</v-icon>
    编辑
  </v-btn>
</v-col>

以及我的编辑函数:

async clickSave() {
  this.editGroup = false;
  this.loading = true;

  let uri = this.endpointPrefix + '/groups/' + this.groupid;
  let data = {
    name: this.groupDataEdited.name,
    description: this.groupDataEdited.description,
    idTariff: this.groupDataEdited.idTariff,
    idEvseToAdd: [],
  };
  await this.$axios.$put(uri, data)
    .then(() => {
      this.$nuxt.$emit('show-notification', {
        text: '已更新',
        color: 'green',
      });
      this.loadData(this.groupid);
    })
    .catch((error) => {
      this.$nuxt.$emit('show-notification', {
        text: '无法更新!' + error.response.data,
        color: 'red',
      });
    })
    .finally(() => (this.loading = false));
  setTimeout(() => {
    this.loading = false;
  }, 5000);
},

至于进度条,我正在使用Vuetify的圆形进度条,就像这样:

<v-progress-circular
  v-show="loading"
  indeterminate
  color="primary"
></v-progress-circular>

但我仍然没有弄清楚应该在我的代码的哪一点放置这个组件才能在整个页面上显示。我尝试将它放在按钮之前,但它根本不起作用(什么也不会发生),如果我将它放在按钮内部,它将永远加载。

英文:

On my page, I have an "edit" button on my page, when I click on it I can edit few things and then save or close without saving.

I want to add a loading bar (like the page is loading) when the user clicks on the "save" button before showing the result.

What I have now is :

the user clicks on the "edit" button, some data become editable and he gets a "save" and a "close" button (that closes without saving), if he clicks on the "save" button the page will reload with the edited data and a notification message "updated" is shown (or an error message if the update didn't go through).

What I would like to have :

when the user clicks on the "save" button after making his edits, the page will show a loading bar (as to show it's loading the data) for a few seconds (let's say for example 5 seconds, if the request is done by that time, the loading will end but if the request isn't done yet, the bar will keep loading until it's done) before showing all the data again and the "updated" notification.

Here's some of the code I have related to this:

My buttons:

             &lt;v-col cols=&quot;3&quot;&gt;
              &lt;v-btn
                v-if=&quot;editGroup&quot;
                light
                class=&quot;mx-2&quot;
                @click=&quot;editGroup = false&quot;
              &gt;
                &lt;v-icon left&gt;mdi-close&lt;/v-icon&gt;
                Close
              &lt;/v-btn&gt;
              &lt;v-btn
                v-if=&quot;editGroup&quot; 
                light 
                class=&quot;mx-2&quot; 
                @click=&quot;clickSave&quot;
                &gt;
                &lt;v-icon left&gt;mdi-content-save&lt;/v-icon&gt;
                Save
              &lt;/v-btn&gt;
              &lt;v-btn
                v-if=&quot;
                  canShowButton([&#39;administrateur&#39;, &#39;configurateur&#39;]) &amp;&amp;
                  !editGroup
                &quot;
                light
                class=&quot;mx-2&quot;
                @click=&quot;editGroup = true&quot;
              &gt;
                &lt;v-icon left&gt;mdi-pencil&lt;/v-icon&gt;
                Edit
              &lt;/v-btn&gt;
            &lt;/v-col&gt;

And my edit fuction :

 async clickSave() {
  this.editGroup = false
  this.loading = true

  let uri = this.endpointPrefix + &#39;/groups/&#39; + this.groupid
  let dat = {
    name: this.groupDataEdited.name,
    description: this.groupDataEdited.description,
    idTariff: this.groupDataEdited.idTariff,
    idEvseToAdd: [],
  }
  await this.$axios.$put(uri, dat)
    .then(() =&gt; {
        this.$nuxt.$emit(&#39;show-notification&#39;, {
          text: &#39;updated&#39;,
          color: &#39;green&#39;,
        })
        this.loadData(this.groupid)
    })
    .catch((error) =&gt; {
      this.$nuxt.$emit(&#39;show-notification&#39;, {
        text:
          &#39;could not be updated !&#39; +
          error.response.data,
        color: &#39;red&#39;,
      })
    })
    .finally(() =&gt; (this.loading = false))              
  setTimeout(() =&gt; {
    this.loading = false        
  }, 5000);
},

As for the progress bar, I am using vuetify progress circular like this :

   &lt;v-progress-circular
    v-show=&quot;loading&quot;
    indeterminate
    color=&quot;primary&quot;&gt;
   &lt;/v-progress-circular&gt;

But I still haven't figured out at what point of my code should I put this component yet to have it shown in all of my page, I tried putting it before the buttons but it doesn't work at all (nothing happens) and if I put it inside the buttons it keeps loading forever.

答案1

得分: 0

以下是您要翻译的内容:

这就是像pinia这样的状态管理的用途。如果您设置一个像这样的pinia存储:

import { defineStore } from "pinia"

export enum PageState {
  LOADING,
  LOADED,
  ERROR
}

export const usePageStore = defineStore({
  state: () => ({
    pageState: PageState.LOADED
  }),
  getters: {},
  actions: {
    saveData(endpointPrefix, groupId, newData) {
      try {
        // 在这里处理所有的URL构建/数据调用工作
      } catch (e) {
        throw new Error(e)
      }
    },
  },
})

然后,不要在您的Vue脚本中执行所有数据处理工作,只需执行以下部分:

pageStore = usePageStore()

async clickSave() {
  try {
    pageStore.pageState = PageState.LOADING
    const response = await pageStore.getData(this.endpointPrefix, this.groupId, dat)
    this.$nuxt.$emit('show-notification', {
      text: 'updated',
      color: 'green',
    })
    this.loadData(this.groupid)
    pageStore.pageState = PageState.LOADED
  } catch (e) {
    pageStore.pageState = PageState.ERROR
    this.$nuxt.$emit('show-notification', {
      text:
        'could not be updated !' +
        error.response.data,
      color: 'red',
    })
  }  
}

然后,您的主要页面视图可以访问相同的存储,监视该变量,并根据需要进行更改以显示您需要的内容。

英文:

This is what state management such as pinia is for. If you set up a pinia store like so:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

import { defineStore } from &quot;pinia&quot;

export enum PageState {
  LOADING,
  LOADED,
  ERROR
}

export const usePageStore = defineStore({
  state: () =&gt; ({
    pageState: PageState.LOADED
  }),
  getters: {},
  actions: {
    saveData(endpointPrefix, groupId, newData) {
      try {
        // Put all your URL building/data calling work here
      } catch (e) {
        throw new Error(e)
      }
    },
  },
})

<!-- end snippet -->

Then, instead of doing all the data work in your vue script, you just do this part:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

pageStore = usePageStore()

async clickSave() {
  try {
    pageStore.pageState = PageState.LOADING
    const response = await pageStore.getData(this.endpointPrefix, this.groupId, dat)
    this.$nuxt.$emit(&#39;show-notification&#39;, {
          text: &#39;updated&#39;,
          color: &#39;green&#39;,
        })
    this.loadData(this.groupid)
    pageStore.pageState = PageState.LOADED
  } catch (e) {
    pageStore.pageState = PageState.ERROR
     this.$nuxt.$emit(&#39;show-notification&#39;, {
        text:
          &#39;could not be updated !&#39; +
          error.response.data,
        color: &#39;red&#39;,
      })
  }  
}

<!-- end snippet -->

Then your main, overarching page view can access the same store, watch that variable, and change according to show whatever you need.

答案2

得分: 0

你最好的选择可能是添加一个叠加层,它可以放在根元素的最底部。

示例代码如下:

<v-col cols="3">
  <v-btn
    v-if="editGroup"
    light
    class="mx-2"
    @click="editGroup = false"
  >
    <v-icon left>mdi-close</v-icon>
    关闭
  </v-btn>
  <v-btn
    v-if="editGroup"
    light
    class="mx-2"
    @click="clickSave"
  >
    <v-icon left>mdi-content-save</v-icon>
    保存
  </v-btn>
  <v-btn
    v-if="canShowButton(['administrateur', 'configurateur']) && !editGroup"
    light
    class="mx-2"
    @click="editGroup = true"
  >
    <v-icon left>mdi-pencil</v-icon>
    编辑
  </v-btn>
  <!-- 只有在 loading 为 false 时才会显示叠加层 -->
  <v-overlay v-model="loading">
    <v-progress-circular
      indeterminate
      color="primary"
    ></v-progress-circular>
  </v-overlay>
</v-col>

叠加层将覆盖整个屏幕,直到 loading 属性标记为 false。

详细信息请参考:Vuetify 叠加层组件

另外,初始加载时,loading 必须在页面的数据部分设置如下:

<script>
  export default {
    data: () => ({
      loading: false,
    }),
  }
</script>
英文:

Your best bet is going to probably be to put in an overlay, which can be placed at the very bottom of your root element.

As a rough example:

         &lt;v-col cols=&quot;3&quot;&gt;
          &lt;v-btn
            v-if=&quot;editGroup&quot;
            light
            class=&quot;mx-2&quot;
            @click=&quot;editGroup = false&quot;
          &gt;
            &lt;v-icon left&gt;mdi-close&lt;/v-icon&gt;
            Close
          &lt;/v-btn&gt;
          &lt;v-btn
            v-if=&quot;editGroup&quot; 
            light 
            class=&quot;mx-2&quot; 
            @click=&quot;clickSave&quot;
            &gt;
            &lt;v-icon left&gt;mdi-content-save&lt;/v-icon&gt;
            Save
          &lt;/v-btn&gt;
          &lt;v-btn
            v-if=&quot;
              canShowButton([&#39;administrateur&#39;, &#39;configurateur&#39;]) &amp;&amp;
              !editGroup
            &quot;
            light
            class=&quot;mx-2&quot;
            @click=&quot;editGroup = true&quot;
          &gt;
            &lt;v-icon left&gt;mdi-pencil&lt;/v-icon&gt;
            Edit
          &lt;/v-btn&gt;
          &lt;--The overlay will not display when loading is false--&gt;
          &lt;v-overlay v-model=&quot;loading&quot;&gt;
            &lt;v-progress-circular
            indeterminate
            color=&quot;primary&quot;&gt;
            &lt;/v-progress-circular&gt; 
          &lt;/v-overlay&gt;
        &lt;/v-col&gt;

The overlay will cover the entire screen until the loading prop is marked as false

https://vuetifyjs.com/en/components/overlays/

Edit:

Additionally, loading will have to be set in the data section of your page when you first load in.

&lt;script&gt;
  export default {
    data: () =&gt; ({
      loading: false,
    }),
    },
  }
&lt;/script&gt;

huangapple
  • 本文由 发表于 2023年6月27日 21:46:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76565530.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定