Browse Source

feat: tanstack query demos (#4276)

* chore(@vben/request): add axios-retry

* feat: error retry

* feat: paginated queries

* feat: infinite queries

* chore: update

* chore: update

* fix: ci error

* chore: update

* chore: remove axios-retry

* chore: update deps

* chore: update deps

* chore: update deps

* chore: update pnpm.lock

---------

Co-authored-by: vince <vince292007@gmail.com>
pull/4353/head
Li Kui 2 weeks ago
committed by GitHub
parent
commit
86ed732ca8
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      playground/package.json
  2. 5
      playground/src/bootstrap.ts
  3. 9
      playground/src/router/routes/modules/demos.ts
  4. 25
      playground/src/views/demos/features/vue-query/index.vue
  5. 58
      playground/src/views/demos/features/vue-query/infinite-queries.vue
  6. 51
      playground/src/views/demos/features/vue-query/paginated-queries.vue
  7. 34
      playground/src/views/demos/features/vue-query/query-retries.vue
  8. 18
      playground/src/views/demos/features/vue-query/typing.ts
  9. 38
      pnpm-lock.yaml

1
playground/package.json

@ -26,6 +26,7 @@
"#/*": "./src/*"
},
"dependencies": {
"@tanstack/vue-query": "^5.53.1",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*",

5
playground/src/bootstrap.ts

@ -5,6 +5,8 @@ import { initStores } from '@vben/stores';
import '@vben/styles';
import '@vben/styles/antd';
import { VueQueryPlugin } from '@tanstack/vue-query';
import { setupI18n } from '#/locales';
import App from './app.vue';
@ -25,6 +27,9 @@ async function bootstrap(namespace: string) {
// 配置路由及路由守卫
app.use(router);
// 配置@tanstack/vue-query
app.use(VueQueryPlugin);
app.mount('#app');
}

9
playground/src/router/routes/modules/demos.ts

@ -183,6 +183,15 @@ const routes: RouteRecordRaw[] = [
title: $t('page.demos.features.clipboard'),
},
},
{
name: 'VueQueryDemo',
path: '/demos/features/vue-query',
component: () =>
import('#/views/demos/features/vue-query/index.vue'),
meta: {
title: 'Tanstack Query',
},
},
],
},
// 面包屑导航

25
playground/src/views/demos/features/vue-query/index.vue

@ -0,0 +1,25 @@
<script setup lang="ts">
import { Page } from '@vben/common-ui';
import { Card } from 'ant-design-vue';
import InfiniteQueries from './infinite-queries.vue';
import PaginatedQueries from './paginated-queries.vue';
import QueryRetries from './query-retries.vue';
</script>
<template>
<Page title="Vue Query示例">
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<Card title="分页查询">
<PaginatedQueries />
</Card>
<Card title="无限滚动">
<InfiniteQueries class="h-[300px] overflow-auto" />
</Card>
<Card title="错误重试">
<QueryRetries />
</Card>
</div>
</Page>
</template>

58
playground/src/views/demos/features/vue-query/infinite-queries.vue

@ -0,0 +1,58 @@
<script setup lang="ts">
import type { IProducts } from './typing';
import { useInfiniteQuery } from '@tanstack/vue-query';
import { Button } from 'ant-design-vue';
const LIMIT = 10;
const fetchProducts = async ({ pageParam = 0 }): Promise<IProducts> => {
const res = await fetch(
`https://dummyjson.com/products?limit=${LIMIT}&skip=${pageParam * LIMIT}`,
);
return res.json();
};
const {
data,
error,
fetchNextPage,
hasNextPage,
isError,
isFetching,
isFetchingNextPage,
isPending,
} = useInfiniteQuery({
getNextPageParam: (current, allPages) => {
const nextPage = allPages.length + 1;
const lastPage = current.skip + current.limit;
if (lastPage === current.total) return;
return nextPage;
},
initialPageParam: 0,
queryFn: fetchProducts,
queryKey: ['products'],
});
</script>
<template>
<div>
<span v-if="isPending">加载...</span>
<span v-else-if="isError">出错了: {{ error }}</span>
<div v-else-if="data">
<span v-if="isFetching && !isFetchingNextPage">Fetching...</span>
<ul v-for="(group, index) in data.pages" :key="index">
<li v-for="product in group.products" :key="product.id">
{{ product.title }}
</li>
</ul>
<Button
:disabled="!hasNextPage || isFetchingNextPage"
@click="() => fetchNextPage()"
>
<span v-if="isFetchingNextPage">加载中...</span>
<span v-else-if="hasNextPage">加载更多</span>
<span v-else>没有更多了</span>
</Button>
</div>
</div>
</template>

51
playground/src/views/demos/features/vue-query/paginated-queries.vue

@ -0,0 +1,51 @@
<script setup lang="ts">
import type { IProducts } from './typing';
import { type Ref, ref } from 'vue';
import { keepPreviousData, useQuery } from '@tanstack/vue-query';
import { Button } from 'ant-design-vue';
const LIMIT = 10;
const fetcher = async (page: Ref<number>): Promise<IProducts> => {
const res = await fetch(
`https://dummyjson.com/products?limit=${LIMIT}&skip=${(page.value - 1) * LIMIT}`,
);
return res.json();
};
const page = ref(1);
const { data, error, isError, isPending, isPlaceholderData } = useQuery({
// The data from the last successful fetch is available while new data is being requested.
placeholderData: keepPreviousData,
queryFn: () => fetcher(page),
queryKey: ['products', page],
});
const prevPage = () => {
page.value = Math.max(page.value - 1, 1);
};
const nextPage = () => {
if (!isPlaceholderData.value) {
page.value = page.value + 1;
}
};
</script>
<template>
<div class="flex gap-4">
<Button size="small" @click="prevPage">上一页</Button>
<p>当前页: {{ page }}</p>
<Button size="small" @click="nextPage">下一页</Button>
</div>
<div class="p-4">
<div v-if="isPending">加载中...</div>
<div v-else-if="isError">出错了: {{ error }}</div>
<div v-else-if="data">
<ul>
<li v-for="item in data.products" :key="item.id">
{{ item.title }}
</li>
</ul>
</div>
</div>
</template>

34
playground/src/views/demos/features/vue-query/query-retries.vue

@ -0,0 +1,34 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useQuery } from '@tanstack/vue-query';
import { Button } from 'ant-design-vue';
const count = ref(-1);
async function fetchApi() {
count.value += 1;
return new Promise((_resolve, reject) => {
setTimeout(() => {
reject(new Error('something went wrong!'));
}, 1000);
});
}
const { error, isFetching, refetch } = useQuery({
enabled: false, // Disable automatic refetching when the query mounts
queryFn: fetchApi,
queryKey: ['queryKey'],
retry: 3, // Will retry failed requests 3 times before displaying an error
});
const onClick = async () => {
count.value = -1;
await refetch();
};
</script>
<template>
<Button :loading="isFetching" @click="onClick"> 发起错误重试 </Button>
<p v-if="count > 0" class="my-3">重试次数{{ count }}</p>
<p>{{ error }}</p>
</template>

18
playground/src/views/demos/features/vue-query/typing.ts

@ -0,0 +1,18 @@
export interface IProducts {
limit: number;
products: {
brand: string;
category: string;
description: string;
discountPercentage: string;
id: string;
images: string[];
price: string;
rating: string;
stock: string;
thumbnail: string;
title: string;
}[];
skip: number;
total: number;
}

38
pnpm-lock.yaml

@ -1156,6 +1156,9 @@ importers:
playground:
dependencies:
'@tanstack/vue-query':
specifier: ^5.53.1
version: 5.54.2(vue@3.5.3(typescript@5.5.4))
'@vben/access':
specifier: workspace:*
version: link:../packages/effects/access
@ -3976,12 +3979,28 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20'
'@tanstack/match-sorter-utils@8.19.4':
resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==}
engines: {node: '>=12'}
'@tanstack/query-core@5.54.1':
resolution: {integrity: sha512-hKS+WRpT5zBFip21pB6Jx1C0hranWQrbv5EJ7qPoiV5MYI3C8rTCqWC9DdBseiPT1JgQWh8Y55YthuYZNiw3Xw==}
'@tanstack/store@0.5.5':
resolution: {integrity: sha512-EOSrgdDAJExbvRZEQ/Xhh9iZchXpMN+ga1Bnk8Nmygzs8TfiE6hbzThF+Pr2G19uHL6+DTDTHhJ8VQiOd7l4tA==}
'@tanstack/virtual-core@3.9.0':
resolution: {integrity: sha512-Saga7/QRGej/IDCVP5BgJ1oDqlDT2d9rQyoflS3fgMS8ntJ8JGw/LBqK2GorHa06+VrNFc0tGz65XQHJQJetFQ==}
'@tanstack/vue-query@5.54.2':
resolution: {integrity: sha512-GYIYee9WkUbPDD28t1kdNNtLCioiIva0MhKCvODGWoEML5MNONCX4/i4y2GGFi8i9nSbcA8MpvD+nt/tdZ+yJw==}
peerDependencies:
'@vue/composition-api': ^1.1.2
vue: 3.5.3
peerDependenciesMeta:
'@vue/composition-api':
optional: true
'@tanstack/vue-store@0.5.5':
resolution: {integrity: sha512-j+CDrxVhtQQNOjWzLmCqJeDwmmTAQGvEaNbLr1uPJ9rxJITodJtFNdBFj7l+Nd5o34v2ayEv64Ugh6+1BtuGNg==}
peerDependencies:
@ -8442,6 +8461,9 @@ packages:
resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==}
engines: {node: '>= 0.10'}
remove-accents@0.5.0:
resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==}
repeat-string@1.6.1:
resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==}
engines: {node: '>=0.10'}
@ -12909,10 +12931,24 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 3.4.10
'@tanstack/match-sorter-utils@8.19.4':
dependencies:
remove-accents: 0.5.0
'@tanstack/query-core@5.54.1': {}
'@tanstack/store@0.5.5': {}
'@tanstack/virtual-core@3.9.0': {}
'@tanstack/vue-query@5.54.2(vue@3.5.3(typescript@5.5.4))':
dependencies:
'@tanstack/match-sorter-utils': 8.19.4
'@tanstack/query-core': 5.54.1
'@vue/devtools-api': 6.6.3
vue: 3.5.3(typescript@5.5.4)
vue-demi: 0.14.10(vue@3.5.3(typescript@5.5.4))
'@tanstack/vue-store@0.5.5(vue@3.5.3(typescript@5.5.4))':
dependencies:
'@tanstack/store': 0.5.5
@ -17933,6 +17969,8 @@ snapshots:
relateurl@0.2.7: {}
remove-accents@0.5.0: {}
repeat-string@1.6.1: {}
require-directory@2.1.1: {}

Loading…
Cancel
Save