diff --git a/package.json b/package.json index ec7722b..fe91c26 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,10 @@ "@varlet/cli": "^2.11.8", "rimraf": "^5.0.1", "typescript": "^5.1.5", - "vue": "^3.3.4" + "vue": "3.3.4" + }, + "peerDependencies": { + "vue": "^3.2.0" }, "repository": { "type": "git", diff --git a/playground/package.json b/playground/package.json index eebbb61..09488fc 100644 --- a/playground/package.json +++ b/playground/package.json @@ -12,9 +12,10 @@ "@varlet/axle": "link:..", "@varlet/touch-emulator": "2.11.8", "@varlet/ui": "^2.11.8", + "@varlet/use": "^2.12.2", "koa": "^2.14.2", - "koa-router": "^12.0.0", "koa-bodyparser": "^4.4.0", + "koa-router": "^12.0.0", "nodemon": "^2.0.15", "unplugin-auto-import": "^0.16.2", "vue": "^3.3.1" diff --git a/playground/pnpm-lock.yaml b/playground/pnpm-lock.yaml index 8224367..83809fd 100644 --- a/playground/pnpm-lock.yaml +++ b/playground/pnpm-lock.yaml @@ -14,6 +14,9 @@ dependencies: '@varlet/ui': specifier: ^2.11.8 version: 2.11.8(vue@3.3.1) + '@varlet/use': + specifier: ^2.12.2 + version: 2.12.2(vue@3.3.1) koa: specifier: ^2.14.2 version: 2.14.2 @@ -353,6 +356,10 @@ packages: resolution: {integrity: sha512-PukW5LrzUyUvIkwqHsytaMZZYVv9P33BCG6fRGgQLS9I+JjJTf/VVo17HjEb3h23IDvqFpgE3034BOtiX1zCYg==} dev: false + /@varlet/shared@2.12.2: + resolution: {integrity: sha512-LSABYZJCZLQHGuC7RmhK7WaChpcWI9HIriQTLLshbflNYB/I+YxAFA7a2MrJndnaN90iCFFIYITwtY3nnDkyBg==} + dev: false + /@varlet/touch-emulator@2.11.8: resolution: {integrity: sha512-yxdVHsZsz/GwtMXsrHTC7hE4UxsWhRkPlJAXKMW55XJpvZHVbxdmdgiyryBlCBtj+BXfqH5f4cGXusccvznzAw==} dev: false @@ -380,6 +387,15 @@ packages: vue: 3.3.1 dev: false + /@varlet/use@2.12.2(vue@3.3.1): + resolution: {integrity: sha512-wDRZ/8UpTscWY/5cJ+2o6qSvl4FiECJoaLpHTK8WZC92j/zUwPJYY6vDIbImsROJW0SN9Ob4zVKJTjTqXC9Rrw==} + peerDependencies: + vue: ^3.2.0 + dependencies: + '@varlet/shared': 2.12.2 + vue: 3.3.1 + dev: false + /@vitejs/plugin-vue@4.2.3(vite@4.3.8)(vue@3.3.1): resolution: {integrity: sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==} engines: {node: ^14.18.0 || >=16.0.0} diff --git a/playground/public/logo.png b/playground/public/logo.png new file mode 100644 index 0000000..2547381 Binary files /dev/null and b/playground/public/logo.png differ diff --git a/playground/src/App.vue b/playground/src/App.vue index f9e54e2..b144481 100644 --- a/playground/src/App.vue +++ b/playground/src/App.vue @@ -6,6 +6,7 @@ import { useApiDeleteUser, useApiUpdateUser, useApiPatchUser, + useApiDownloadFile, User, } from './apis' @@ -13,7 +14,16 @@ const id = ref('1') const deleteId = ref('1') const [users, apiGetUsers, { loading: isUsersLoading }] = useApiGetUsers([], { immediate: true }) -const [user, apiGetUser, { loading: isUserLoading }] = useApiGetUser({}) +const [user, apiGetUser, { loading: isUserLoading, abort }] = useApiGetUser( + {}, + { + onSuccess(response) { + if (response.code === 200) { + Snackbar.success('Getting Success!') + } + }, + } +) const [addedUser, apiAddUser] = useApiAddUser( {}, { @@ -66,6 +76,9 @@ const [deletedUser, apiDeleteUser] = useApiDeleteUser( }, } ) +const [file, apiDownloadFile, { downloadProgress }] = useApiDownloadFile(null, { + onTransform: (response) => response, +}) const userRecord = reactive({ id: '', @@ -100,6 +113,7 @@ watch( Search + Abort name: getUser @@ -125,4 +139,12 @@ watch( Delete + + + + +

Download Progress: {{ downloadProgress * 100 }} %

+

File Size: {{ file?.size ?? 0 }}

+ Download (PS: Please use slow 3G) +
diff --git a/playground/src/apis/index.ts b/playground/src/apis/index.ts index 20236ea..3f3bae5 100644 --- a/playground/src/apis/index.ts +++ b/playground/src/apis/index.ts @@ -67,3 +67,12 @@ export function useApiPatchUser(data: D, options?: Options> ...options, }) } + +export function useApiDownloadFile(data: D, options?: Options) { + return useAxle({ + data, + url: 'http://localhost:5173/logo.png', + runner: axle.getBlob, + ...options, + }) +} \ No newline at end of file diff --git a/playground/src/request/index.ts b/playground/src/request/index.ts index 276dd0e..332fef3 100644 --- a/playground/src/request/index.ts +++ b/playground/src/request/index.ts @@ -11,7 +11,7 @@ const useAxle = createUseAxle({ axle.axios.interceptors.response.use( (response) => { - if (response.data.code !== 200) { + if (response.data.code !== 200 && response.data.message) { Snackbar.warning(response.data.message) } diff --git a/playground/src/server.js b/playground/src/server.js index 3019163..74da818 100644 --- a/playground/src/server.js +++ b/playground/src/server.js @@ -42,7 +42,7 @@ router.get('/user/list-user', async (ctx) => { }) router.get('/user/get-user', async (ctx) => { - await delay(300) + await delay(2000) const user = users.find((user) => user.id === Number(ctx.request.query.id)) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c99175..a5f320e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,8 +20,8 @@ devDependencies: specifier: ^6.9.7 version: 6.9.7 '@varlet/cli': - specifier: ^2.11.1 - version: 2.11.1(@varlet/icons@2.11.1)(@varlet/touch-emulator@2.11.1)(@vue/runtime-core@3.2.47)(@vue/test-utils@2.3.2)(clipboard@2.0.11)(live-server@1.2.2)(lodash-es@4.17.21)(vue-router@4.2.0)(vue@3.3.4) + specifier: ^2.11.8 + version: 2.11.8(@varlet/icons@2.11.8)(@varlet/touch-emulator@2.11.8)(@vue/runtime-core@3.2.47)(@vue/test-utils@2.3.2)(clipboard@2.0.11)(live-server@1.2.2)(lodash-es@4.17.21)(vue-router@4.2.0)(vue@3.3.4) rimraf: specifier: ^5.0.1 version: 5.0.1 @@ -29,7 +29,7 @@ devDependencies: specifier: ^5.1.5 version: 5.1.5 vue: - specifier: ^3.3.4 + specifier: 3.3.4 version: 3.3.4 packages: @@ -1773,6 +1773,7 @@ packages: /@sinonjs/fake-timers@10.2.0: resolution: {integrity: sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==} + deprecated: Use version 10.1.0. Version 10.2.0 has potential breaking issues dependencies: '@sinonjs/commons': 3.0.0 dev: true @@ -1898,13 +1899,13 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@varlet/cli@2.11.1(@varlet/icons@2.11.1)(@varlet/touch-emulator@2.11.1)(@vue/runtime-core@3.2.47)(@vue/test-utils@2.3.2)(clipboard@2.0.11)(live-server@1.2.2)(lodash-es@4.17.21)(vue-router@4.2.0)(vue@3.3.4): - resolution: {integrity: sha512-0wNFCDytAoJu4wsPW9OeoQMPrblWwMOETyZ6m1Tof+HBg285h69x3dvd51kuI7hBs6Ee3NUtl5E4VQ64tmCq7w==} + /@varlet/cli@2.11.8(@varlet/icons@2.11.8)(@varlet/touch-emulator@2.11.8)(@vue/runtime-core@3.2.47)(@vue/test-utils@2.3.2)(clipboard@2.0.11)(live-server@1.2.2)(lodash-es@4.17.21)(vue-router@4.2.0)(vue@3.3.4): + resolution: {integrity: sha512-a8HqNkGNhTvXEQ33+96t1+jFJmCReoZg0j4sy2Lgvks+kUPtV9Lv6J2sZOcwUQsanYKOcy2J0sf82tQ5hjARsg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: - '@varlet/icons': 2.11.1 - '@varlet/touch-emulator': 2.11.1 + '@varlet/icons': 2.11.8 + '@varlet/touch-emulator': 2.11.8 '@vue/runtime-core': 3.2.47 '@vue/test-utils': 2.3.2 clipboard: ^2.0.6 @@ -1918,10 +1919,10 @@ packages: '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.21.8) '@babel/preset-env': 7.18.6(@babel/core@7.21.8) '@babel/preset-typescript': 7.18.6(@babel/core@7.21.8) - '@varlet/icons': 2.11.1 - '@varlet/shared': 2.11.1 - '@varlet/touch-emulator': 2.11.1 - '@varlet/vite-plugins': 2.11.1 + '@varlet/icons': 2.11.8 + '@varlet/shared': 2.11.8 + '@varlet/touch-emulator': 2.11.8 + '@varlet/vite-plugins': 2.11.8 '@vitejs/plugin-vue': 4.2.2(vite@4.3.5)(vue@3.3.4) '@vitejs/plugin-vue-jsx': 3.0.1(vite@4.3.5)(vue@3.3.4) '@vue/babel-plugin-jsx': 1.1.1(@babel/core@7.21.8) @@ -1970,26 +1971,21 @@ packages: - utf-8-validate dev: true - /@varlet/icons@2.11.1: - resolution: {integrity: sha512-J/etC9Sc+M+1jZJDQje3fB20KUeweXuIKxR+BMHfSZmDaeMEf7o3ATStQp2CWJxTSJE9FC5VtEgVaXEwkBK4cg==} - dev: true - - /@varlet/shared@2.11.1: - resolution: {integrity: sha512-QAPtVWtIuZwy0+TPGc7wzVLo6aghiMXayAkFtcJXooIzEaNXOTF69Xljf9UR2gvMYSPiyZDylPf5dRsEepFiJQ==} + /@varlet/icons@2.11.8: + resolution: {integrity: sha512-LbpzNzPjlZR8O4niz5Vf6DHiVuaaa7GHYnKE/0K13hQ+YraV4bGzVriM1vNVXtSzLEphYrUjnG+c0znnR7XgkA==} dev: true /@varlet/shared@2.11.8: resolution: {integrity: sha512-PukW5LrzUyUvIkwqHsytaMZZYVv9P33BCG6fRGgQLS9I+JjJTf/VVo17HjEb3h23IDvqFpgE3034BOtiX1zCYg==} - dev: false - /@varlet/touch-emulator@2.11.1: - resolution: {integrity: sha512-qQ8b4NTU0icAr8FpWnjj9JQVfy6gasG7fAP90OeIC4J8TtkuikX8emdAt5ulgluLwsHnTtbmnbez4fULDAY8Tg==} + /@varlet/touch-emulator@2.11.8: + resolution: {integrity: sha512-yxdVHsZsz/GwtMXsrHTC7hE4UxsWhRkPlJAXKMW55XJpvZHVbxdmdgiyryBlCBtj+BXfqH5f4cGXusccvznzAw==} dev: true - /@varlet/vite-plugins@2.11.1: - resolution: {integrity: sha512-IuvcbxCnL594EMYmoFxw28iMsmrC7QCXOdrYhF99Sfl3tGhSGj8Af7Oc7JBCHFybZczp8RahS/PZzqTMFgm6Vg==} + /@varlet/vite-plugins@2.11.8: + resolution: {integrity: sha512-rmpOxp2WwnPSS8KkAmBomUgfgSz5E9KltNUKzN6lzrSuCOseH0WjcU+/4gqtpxWSQrhmMiOp595zoGbsk4ZHVQ==} dependencies: - '@varlet/shared': 2.11.1 + '@varlet/shared': 2.11.8 ejs: 3.1.8 fs-extra: 9.1.0 highlight.js: 10.7.3 diff --git a/src/use.ts b/src/use.ts index b65f882..25dfdae 100644 --- a/src/use.ts +++ b/src/use.ts @@ -1,4 +1,4 @@ -import { ref, type Ref, type UnwrapRef } from 'vue' +import { ref, type Ref } from 'vue' import { type AxleRequestConfig, createFetchHelper, createModifyHelper } from './instance.js' export interface RunOptions

{ @@ -7,7 +7,7 @@ export interface RunOptions

{ config?: AxleRequestConfig } -export type Run = (options?: RunOptions

) => Promise> +export type Run = (options?: RunOptions

) => Promise export interface UseAxleOptions> { url: string @@ -18,17 +18,20 @@ export interface UseAxleOptions> { immediate?: boolean onBefore?(): void onAfter?(): void - onTransform?(response: UnwrapRef, prev: UnwrapRef): UnwrapRef - onSuccess?(response: UnwrapRef): void + onTransform?(response: R, prev: D): D + onSuccess?(response: R): void onError?(error: Error): void } export type UseAxleReturn = [ - data: Ref>, + data: Ref, run: Run, extra: { - loading: Ref> - error: Ref> + uploadProgress: Ref + downloadProgress: Ref + loading: Ref + error: Ref + abort(): void } ] @@ -38,10 +41,8 @@ export interface CreateUseAxleOptions { export function createUseAxle(options: CreateUseAxleOptions = {}) { const { onTransform: defaultOnTransform } = options - - const useAxle = >( - options: UseAxleOptions - ): UseAxleReturn => { + + const useAxle = >(options: UseAxleOptions) => { const { url, runner, @@ -51,28 +52,57 @@ export function createUseAxle(options: CreateUseAxleOptions = {}) { config: initialConfig, onBefore = () => {}, onAfter = () => {}, - onTransform = (defaultOnTransform as UseAxleOptions['onTransform']) ?? ((response) => response as unknown as UnwrapRef), + onTransform = (defaultOnTransform as UseAxleOptions['onTransform']) ?? + ((response) => response as unknown as D), onSuccess = () => {}, onError = () => {}, } = options + const initialUrl = url - const data = ref(initialData) + const data = ref(initialData) as Ref const loading = ref(false) const error = ref() + const downloadProgress = ref(0) + const uploadProgress = ref(0) + + let controller = new AbortController() + + const abort = () => { + controller.abort() + } + + const run: Run = (options: RunOptions

= {}) => { + if (controller.signal.aborted) { + controller = new AbortController() + } + + uploadProgress.value = 0 + downloadProgress.value = 0 - const run: Run = (options: RunOptions

= {}): Promise> => { const url = options.url ?? initialUrl onBefore() loading.value = true - return runner(url, options.params, options.config) + return runner(url, options.params, { + signal: controller.signal, + + onUploadProgress(event) { + uploadProgress.value = event.progress ?? 0 + }, + + onDownloadProgress(event) { + downloadProgress.value = event.progress ?? 0 + }, + + ...options.config, + }) .then((response) => { - data.value = onTransform(response as UnwrapRef, data.value) + data.value = onTransform(response as R, data.value) error.value = undefined - onSuccess(response as UnwrapRef) + onSuccess(response as R) return data.value }) @@ -103,8 +133,11 @@ export function createUseAxle(options: CreateUseAxleOptions = {}) { { loading, error, + uploadProgress, + downloadProgress, + abort, }, - ] + ] as UseAxleReturn } return useAxle