@movk/nuxt 1.1.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nuxt/movk-ui.css +3 -0
- package/README.md +75 -104
- package/dist/module.d.mts +59 -1
- package/dist/module.json +2 -2
- package/dist/module.mjs +136 -56
- package/dist/runtime/components/AutoForm.d.vue.ts +10 -31
- package/dist/runtime/components/AutoForm.vue +114 -148
- package/dist/runtime/components/AutoForm.vue.d.ts +10 -31
- package/dist/runtime/components/ColorChooser.d.vue.ts +20 -17
- package/dist/runtime/components/ColorChooser.vue +305 -31
- package/dist/runtime/components/ColorChooser.vue.d.ts +20 -17
- package/dist/runtime/components/DataTable.d.vue.ts +57 -0
- package/dist/runtime/components/DataTable.vue +584 -0
- package/dist/runtime/components/DataTable.vue.d.ts +57 -0
- package/dist/runtime/components/DatePicker.d.vue.ts +15 -22
- package/dist/runtime/components/DatePicker.vue +173 -65
- package/dist/runtime/components/DatePicker.vue.d.ts +15 -22
- package/dist/runtime/components/MessageBox.d.vue.ts +36 -0
- package/dist/runtime/components/MessageBox.vue +113 -0
- package/dist/runtime/components/MessageBox.vue.d.ts +36 -0
- package/dist/runtime/components/PillGroup.d.vue.ts +33 -0
- package/dist/runtime/components/PillGroup.vue +291 -0
- package/dist/runtime/components/PillGroup.vue.d.ts +33 -0
- package/dist/runtime/components/Popconfirm.d.vue.ts +30 -0
- package/dist/runtime/components/Popconfirm.vue +143 -0
- package/dist/runtime/components/Popconfirm.vue.d.ts +30 -0
- package/dist/runtime/components/SearchForm.d.vue.ts +43 -0
- package/dist/runtime/components/SearchForm.vue +274 -0
- package/dist/runtime/components/SearchForm.vue.d.ts +43 -0
- package/dist/runtime/components/SlideVerify.d.vue.ts +13 -79
- package/dist/runtime/components/SlideVerify.vue +117 -90
- package/dist/runtime/components/SlideVerify.vue.d.ts +13 -79
- package/dist/runtime/components/StarRating.d.vue.ts +25 -82
- package/dist/runtime/components/StarRating.vue +128 -99
- package/dist/runtime/components/StarRating.vue.d.ts +25 -82
- package/dist/runtime/components/input/AsPhoneNumberInput.d.vue.ts +32 -0
- package/dist/runtime/components/input/AsPhoneNumberInput.vue +48 -0
- package/dist/runtime/components/input/AsPhoneNumberInput.vue.d.ts +32 -0
- package/dist/runtime/components/input/WithCharacterLimit.d.vue.ts +13 -7
- package/dist/runtime/components/input/WithCharacterLimit.vue +27 -15
- package/dist/runtime/components/input/WithCharacterLimit.vue.d.ts +13 -7
- package/dist/runtime/components/input/WithClear.d.vue.ts +11 -5
- package/dist/runtime/components/input/WithClear.vue +29 -10
- package/dist/runtime/components/input/WithClear.vue.d.ts +11 -5
- package/dist/runtime/components/input/WithCopy.d.vue.ts +13 -7
- package/dist/runtime/components/input/WithCopy.vue +28 -11
- package/dist/runtime/components/input/WithCopy.vue.d.ts +13 -7
- package/dist/runtime/components/input/WithFloatingLabel.d.vue.ts +33 -0
- package/dist/runtime/components/input/WithFloatingLabel.vue +75 -0
- package/dist/runtime/components/input/WithFloatingLabel.vue.d.ts +33 -0
- package/dist/runtime/components/input/WithPasswordToggle.d.vue.ts +13 -7
- package/dist/runtime/components/input/WithPasswordToggle.vue +27 -10
- package/dist/runtime/components/input/WithPasswordToggle.vue.d.ts +13 -7
- package/dist/runtime/components/theme-picker/ThemePicker.d.vue.ts +8 -1
- package/dist/runtime/components/theme-picker/ThemePicker.vue +37 -28
- package/dist/runtime/components/theme-picker/ThemePicker.vue.d.ts +8 -1
- package/dist/runtime/components/theme-picker/ThemePickerButton.d.vue.ts +5 -6
- package/dist/runtime/components/theme-picker/ThemePickerButton.vue +4 -0
- package/dist/runtime/components/theme-picker/ThemePickerButton.vue.d.ts +5 -6
- package/dist/runtime/composables/index.d.ts +9 -8
- package/dist/runtime/composables/index.js +2 -1
- package/dist/runtime/composables/useApiFetch.d.ts +23 -15
- package/dist/runtime/composables/useApiFetch.js +21 -28
- package/dist/runtime/composables/useAutoForm.d.ts +14 -98
- package/dist/runtime/composables/useAutoForm.js +55 -161
- package/dist/runtime/composables/useClientApiFetch.d.ts +7 -7
- package/dist/runtime/composables/useDateFormatter.d.ts +16 -3
- package/dist/runtime/composables/useDateFormatter.js +89 -30
- package/dist/runtime/composables/useDownloadWithProgress.d.ts +26 -27
- package/dist/runtime/composables/useDownloadWithProgress.js +143 -42
- package/dist/runtime/composables/useLazyApiFetch.d.ts +19 -0
- package/dist/runtime/composables/useLazyApiFetch.js +4 -0
- package/dist/runtime/composables/useMessageBox.d.ts +6 -0
- package/dist/runtime/composables/useMessageBox.js +16 -0
- package/dist/runtime/composables/useTheme.d.ts +21 -8
- package/dist/runtime/composables/useTheme.js +109 -86
- package/dist/runtime/composables/useUploadWithProgress.d.ts +22 -28
- package/dist/runtime/composables/useUploadWithProgress.js +79 -68
- package/dist/runtime/domains/api/auth.d.ts +2 -0
- package/dist/runtime/domains/api/auth.js +31 -0
- package/dist/runtime/domains/api/endpoint-config.d.ts +11 -0
- package/dist/runtime/domains/api/endpoint-config.js +17 -0
- package/dist/runtime/domains/api/errors.d.ts +2 -0
- package/dist/runtime/domains/api/errors.js +10 -0
- package/dist/runtime/domains/api/fetch-key.d.ts +20 -0
- package/dist/runtime/domains/api/fetch-key.js +23 -0
- package/dist/runtime/domains/api/interceptors/error.d.ts +13 -0
- package/dist/runtime/domains/api/interceptors/error.js +49 -0
- package/dist/runtime/domains/api/interceptors/request.d.ts +12 -0
- package/dist/runtime/domains/api/interceptors/request.js +46 -0
- package/dist/runtime/domains/api/interceptors/response.d.ts +17 -0
- package/dist/runtime/domains/api/interceptors/response.js +27 -0
- package/dist/runtime/domains/api/response.d.ts +4 -0
- package/dist/runtime/domains/api/response.js +14 -0
- package/dist/runtime/domains/api/toast.d.ts +15 -0
- package/dist/runtime/domains/api/toast.js +46 -0
- package/dist/runtime/domains/api/transfer.d.ts +69 -0
- package/dist/runtime/domains/api/transfer.js +81 -0
- package/dist/runtime/domains/auto-form/actions.d.ts +2 -0
- package/dist/runtime/domains/auto-form/actions.js +4 -0
- package/dist/runtime/domains/auto-form/components/Array.d.vue.ts +13 -0
- package/dist/runtime/{components/auto-form-renderer/AutoFormRendererArray.vue → domains/auto-form/components/Array.vue} +37 -48
- package/dist/runtime/domains/auto-form/components/Array.vue.d.ts +13 -0
- package/dist/runtime/domains/auto-form/components/Children.d.vue.ts +11 -0
- package/dist/runtime/domains/auto-form/components/Children.vue +50 -0
- package/dist/runtime/domains/auto-form/components/Children.vue.d.ts +11 -0
- package/dist/runtime/domains/auto-form/components/Field.d.vue.ts +11 -0
- package/dist/runtime/domains/auto-form/components/Field.vue +56 -0
- package/dist/runtime/domains/auto-form/components/Field.vue.d.ts +11 -0
- package/dist/runtime/domains/auto-form/components/Layout.d.vue.ts +11 -0
- package/dist/runtime/domains/auto-form/components/Layout.vue +89 -0
- package/dist/runtime/domains/auto-form/components/Layout.vue.d.ts +11 -0
- package/dist/runtime/domains/auto-form/components/Nested.d.vue.ts +11 -0
- package/dist/runtime/domains/auto-form/components/Nested.vue +47 -0
- package/dist/runtime/domains/auto-form/components/Nested.vue.d.ts +11 -0
- package/dist/runtime/{constants/auto-form.d.ts → domains/auto-form/constants.d.ts} +0 -2
- package/dist/runtime/{constants/auto-form.js → domains/auto-form/constants.js} +0 -25
- package/dist/runtime/domains/auto-form/controls.d.ts +41 -0
- package/dist/runtime/domains/auto-form/controls.js +73 -0
- package/dist/runtime/{utils/field-utils.d.ts → domains/auto-form/fields.d.ts} +7 -21
- package/dist/runtime/{utils/field-utils.js → domains/auto-form/fields.js} +19 -4
- package/dist/runtime/domains/auto-form/metadata.d.ts +22 -0
- package/dist/runtime/domains/auto-form/metadata.js +53 -0
- package/dist/runtime/domains/auto-form/provider.d.ts +62 -0
- package/dist/runtime/{internal/useAutoFormProvider.js → domains/auto-form/provider.js} +6 -3
- package/dist/runtime/domains/auto-form/reactive.d.ts +12 -0
- package/dist/runtime/{utils/reactive-utils.js → domains/auto-form/reactive.js} +1 -1
- package/dist/runtime/domains/auto-form/schema.d.ts +7 -0
- package/dist/runtime/{utils/schema-introspector.js → domains/auto-form/schema.js} +12 -10
- package/dist/runtime/domains/data-table/columns/constants.d.ts +50 -0
- package/dist/runtime/domains/data-table/columns/constants.js +19 -0
- package/dist/runtime/domains/data-table/columns/resolve-columns.d.ts +4 -0
- package/dist/runtime/domains/data-table/columns/resolve-columns.js +59 -0
- package/dist/runtime/domains/data-table/columns/resolve-data-column.d.ts +9 -0
- package/dist/runtime/domains/data-table/columns/resolve-data-column.js +164 -0
- package/dist/runtime/domains/data-table/columns/resolve-group-column.d.ts +4 -0
- package/dist/runtime/domains/data-table/columns/resolve-group-column.js +19 -0
- package/dist/runtime/domains/data-table/columns/resolve-special-columns.d.ts +8 -0
- package/dist/runtime/domains/data-table/columns/resolve-special-columns.js +232 -0
- package/dist/runtime/domains/data-table/columns/style.d.ts +11 -0
- package/dist/runtime/domains/data-table/columns/style.js +67 -0
- package/dist/runtime/domains/data-table/columns/utils.d.ts +6 -0
- package/dist/runtime/domains/data-table/columns/utils.js +16 -0
- package/dist/runtime/domains/data-table/components/ActionConfirm.d.vue.ts +10 -0
- package/dist/runtime/domains/data-table/components/ActionConfirm.vue +39 -0
- package/dist/runtime/domains/data-table/components/ActionConfirm.vue.d.ts +10 -0
- package/dist/runtime/domains/data-table/components/ActionsCell.d.vue.ts +11 -0
- package/dist/runtime/domains/data-table/components/ActionsCell.vue +91 -0
- package/dist/runtime/domains/data-table/components/ActionsCell.vue.d.ts +11 -0
- package/dist/runtime/domains/data-table/components/CellTooltip.d.vue.ts +9 -0
- package/dist/runtime/domains/data-table/components/CellTooltip.vue +40 -0
- package/dist/runtime/domains/data-table/components/CellTooltip.vue.d.ts +9 -0
- package/dist/runtime/domains/data-table/components/Pagination.d.vue.ts +26 -0
- package/dist/runtime/domains/data-table/components/Pagination.vue +132 -0
- package/dist/runtime/domains/data-table/components/Pagination.vue.d.ts +26 -0
- package/dist/runtime/domains/data-table/indent.d.ts +8 -0
- package/dist/runtime/domains/data-table/indent.js +19 -0
- package/dist/runtime/domains/data-table/tree-selection.d.ts +9 -0
- package/dist/runtime/domains/data-table/tree-selection.js +76 -0
- package/dist/runtime/{utils/theme.d.ts → domains/theme/theme-icons.d.ts} +0 -1
- package/dist/runtime/index.css +1 -0
- package/dist/runtime/plugins/api.factory.js +28 -171
- package/dist/runtime/plugins/theme.js +72 -70
- package/dist/runtime/types/api/config.d.ts +127 -0
- package/dist/runtime/types/api/fetch.d.ts +50 -0
- package/dist/runtime/types/api/index.d.ts +5 -0
- package/dist/runtime/types/api/module.d.ts +94 -0
- package/dist/runtime/types/api/response.d.ts +62 -0
- package/dist/runtime/types/api/transfer.d.ts +32 -0
- package/dist/runtime/types/api/transfer.js +0 -0
- package/dist/runtime/types/app.config.d.ts +6 -0
- package/dist/runtime/types/auto-form/base.d.ts +26 -0
- package/dist/runtime/types/auto-form/base.js +0 -0
- package/dist/runtime/types/auto-form/component.d.ts +28 -0
- package/dist/runtime/types/auto-form/component.js +0 -0
- package/dist/runtime/types/auto-form/controls.d.ts +45 -0
- package/dist/runtime/types/auto-form/controls.js +0 -0
- package/dist/runtime/types/auto-form/fields.d.ts +68 -0
- package/dist/runtime/types/auto-form/fields.js +0 -0
- package/dist/runtime/types/auto-form/index.d.ts +7 -0
- package/dist/runtime/types/auto-form/index.js +0 -0
- package/dist/runtime/types/auto-form/search-form.d.ts +84 -0
- package/dist/runtime/types/auto-form/search-form.js +0 -0
- package/dist/runtime/types/auto-form/slots.d.ts +85 -0
- package/dist/runtime/types/auto-form/slots.js +0 -0
- package/dist/runtime/types/auto-form/zod-factory.d.ts +127 -0
- package/dist/runtime/types/auto-form/zod-factory.js +0 -0
- package/dist/runtime/types/components/color-chooser.d.ts +109 -0
- package/dist/runtime/types/components/color-chooser.js +0 -0
- package/dist/runtime/types/components/date-picker.d.ts +41 -0
- package/dist/runtime/types/components/date-picker.js +0 -0
- package/dist/runtime/types/components/index.d.ts +8 -0
- package/dist/runtime/types/components/index.js +0 -0
- package/dist/runtime/types/components/input/as-phone-number-input.d.ts +17 -0
- package/dist/runtime/types/components/input/as-phone-number-input.js +0 -0
- package/dist/runtime/types/components/input/index.d.ts +6 -0
- package/dist/runtime/types/components/input/index.js +0 -0
- package/dist/runtime/types/components/input/with-character-limit.d.ts +11 -0
- package/dist/runtime/types/components/input/with-character-limit.js +0 -0
- package/dist/runtime/types/components/input/with-clear.d.ts +10 -0
- package/dist/runtime/types/components/input/with-clear.js +0 -0
- package/dist/runtime/types/components/input/with-copy.d.ts +11 -0
- package/dist/runtime/types/components/input/with-copy.js +0 -0
- package/dist/runtime/types/components/input/with-floating-label.d.ts +12 -0
- package/dist/runtime/types/components/input/with-floating-label.js +0 -0
- package/dist/runtime/types/components/input/with-password-toggle.d.ts +7 -0
- package/dist/runtime/types/components/input/with-password-toggle.js +0 -0
- package/dist/runtime/types/components/message-box.d.ts +69 -0
- package/dist/runtime/types/components/message-box.js +0 -0
- package/dist/runtime/types/components/pill-group.d.ts +103 -0
- package/dist/runtime/types/components/pill-group.js +0 -0
- package/dist/runtime/types/components/popconfirm.d.ts +74 -0
- package/dist/runtime/types/components/popconfirm.js +0 -0
- package/dist/runtime/types/components/slide-verify.d.ts +54 -0
- package/dist/runtime/types/components/slide-verify.js +0 -0
- package/dist/runtime/types/components/star-rating.d.ts +55 -0
- package/dist/runtime/types/components/star-rating.js +0 -0
- package/dist/runtime/types/data-table/columns.d.ts +236 -0
- package/dist/runtime/types/data-table/columns.js +6 -0
- package/dist/runtime/types/data-table/component.d.ts +190 -0
- package/dist/runtime/types/data-table/component.js +0 -0
- package/dist/runtime/types/data-table/contexts.d.ts +44 -0
- package/dist/runtime/types/data-table/contexts.js +0 -0
- package/dist/runtime/types/data-table/index.d.ts +6 -0
- package/dist/runtime/types/data-table/index.js +1 -0
- package/dist/runtime/types/data-table/pagination.d.ts +87 -0
- package/dist/runtime/types/data-table/pagination.js +0 -0
- package/dist/runtime/types/index.d.ts +6 -5
- package/dist/runtime/types/index.js +5 -5
- package/dist/runtime/types/shared.d.ts +5 -0
- package/dist/runtime/types/shared.js +0 -0
- package/dist/runtime/types/zod.d.ts +19 -17
- package/dist/runtime/utils/extend-theme.d.ts +19 -0
- package/dist/runtime/utils/extend-theme.js +44 -0
- package/dist/runtime/utils/form-control.d.ts +33 -0
- package/dist/runtime/utils/form-control.js +54 -0
- package/dist/runtime/utils/meta.d.ts +4 -0
- package/dist/runtime/utils/meta.js +14 -0
- package/dist/runtime/utils/theme-defaults.d.ts +27 -0
- package/dist/runtime/utils/theme-defaults.js +28 -0
- package/dist/runtime/utils/tv.d.ts +1 -0
- package/dist/runtime/utils/tv.js +4 -0
- package/dist/runtime/vue/composables/useSiteConfig.d.ts +13 -0
- package/dist/runtime/vue/composables/useSiteConfig.js +11 -0
- package/dist/runtime/vue/plugins/theme.d.ts +6 -0
- package/dist/runtime/vue/plugins/theme.js +14 -0
- package/dist/runtime/vue/stubs/base.d.ts +2 -0
- package/dist/runtime/vue/stubs/base.js +2 -0
- package/dist/runtime/vue/stubs/inertia.d.ts +2 -0
- package/dist/runtime/vue/stubs/inertia.js +2 -0
- package/dist/runtime/vue/stubs/movk-extra.d.ts +2 -0
- package/dist/runtime/vue/stubs/movk-extra.js +2 -0
- package/dist/runtime/vue/stubs/none.d.ts +2 -0
- package/dist/runtime/vue/stubs/none.js +2 -0
- package/dist/runtime/vue/stubs/vue-router.d.ts +2 -0
- package/dist/runtime/vue/stubs/vue-router.js +2 -0
- package/dist/shared/nuxt.DfBEyjld.mjs +667 -0
- package/dist/types.d.mts +2 -6
- package/dist/unplugin.d.mts +28 -0
- package/dist/unplugin.mjs +292 -0
- package/dist/vite.d.mts +12 -0
- package/dist/vite.mjs +20 -0
- package/package.json +82 -42
- package/vue-plugin.d.ts +5 -0
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererArray.d.vue.ts +0 -20
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererArray.vue.d.ts +0 -20
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererField.d.vue.ts +0 -20
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererField.vue +0 -55
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererField.vue.d.ts +0 -20
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererLayout.d.vue.ts +0 -20
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererLayout.vue +0 -125
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererLayout.vue.d.ts +0 -20
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererNested.d.vue.ts +0 -20
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererNested.vue +0 -105
- package/dist/runtime/components/auto-form-renderer/AutoFormRendererNested.vue.d.ts +0 -20
- package/dist/runtime/composables/useApiAuth.d.ts +0 -47
- package/dist/runtime/composables/useApiAuth.js +0 -66
- package/dist/runtime/internal/useAutoFormProvider.d.ts +0 -50
- package/dist/runtime/schemas/api.d.ts +0 -590
- package/dist/runtime/schemas/api.js +0 -228
- package/dist/runtime/server/api/_movk/session.post.d.ts +0 -10
- package/dist/runtime/server/api/_movk/session.post.js +0 -18
- package/dist/runtime/style.css +0 -1
- package/dist/runtime/types/api.d.ts +0 -218
- package/dist/runtime/types/auth.d.ts +0 -34
- package/dist/runtime/types/auto-form-renderer.d.ts +0 -22
- package/dist/runtime/types/auto-form.d.ts +0 -548
- package/dist/runtime/types/components.d.ts +0 -43
- package/dist/runtime/types/module.d.ts +0 -39
- package/dist/runtime/utils/api-utils.d.ts +0 -79
- package/dist/runtime/utils/api-utils.js +0 -127
- package/dist/runtime/utils/auto-form.d.ts +0 -3
- package/dist/runtime/utils/auto-form.js +0 -18
- package/dist/runtime/utils/reactive-utils.d.ts +0 -30
- package/dist/runtime/utils/schema-introspector.d.ts +0 -13
- /package/dist/runtime/{utils/theme.js → domains/theme/theme-icons.js} +0 -0
- /package/dist/runtime/types/{api.js → api/config.js} +0 -0
- /package/dist/runtime/types/{auto-form-renderer.js → api/fetch.js} +0 -0
- /package/dist/runtime/types/{auto-form.js → api/index.js} +0 -0
- /package/dist/runtime/types/{module.js → api/module.js} +0 -0
- /package/dist/runtime/types/{components.js → api/response.js} +0 -0
|
@@ -1,52 +1,46 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NitroFetchRequest } from 'nitropack/types';
|
|
2
|
+
import type { ApiError, TransferRequestOptions, TransferResult } from '../types/api';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* 上传选项(带进度监控)
|
|
4
5
|
*/
|
|
5
|
-
export interface UploadWithProgressOptions {
|
|
6
|
+
export interface UploadWithProgressOptions<T = unknown> extends TransferRequestOptions {
|
|
6
7
|
/** 文件字段名 @defaultValue 'file' */
|
|
7
8
|
fieldName?: string;
|
|
8
9
|
/** 额外的表单字段 */
|
|
9
10
|
fields?: Record<string, string | Blob>;
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
/** 上传失败回调 */
|
|
19
|
-
onError?: (error: Error) => void;
|
|
11
|
+
/** 超时毫秒数,0 或未设置不超时 */
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
/** 是否跨域携带凭证 */
|
|
14
|
+
withCredentials?: boolean;
|
|
15
|
+
/** 上传成功回调(解包后业务数据) */
|
|
16
|
+
onSuccess?: (data: T) => void;
|
|
17
|
+
/** 上传失败回调;中止时不触发 */
|
|
18
|
+
onError?: (error: ApiError | Error) => void;
|
|
20
19
|
}
|
|
21
20
|
/**
|
|
22
|
-
* 带进度监控的文件上传 composable
|
|
21
|
+
* 带进度监控的文件上传 composable(仅客户端)
|
|
23
22
|
*
|
|
24
|
-
* 基于原生 XMLHttpRequest
|
|
23
|
+
* @description 基于原生 XMLHttpRequest 实现,复用 `@movk/nuxt` 的端点/认证/业务校验/toast/hook 体系。
|
|
24
|
+
* 返回的 `data` 是按 `dataKey` 解包后的业务数据,与 `useApiFetch<T>` 一致。
|
|
25
25
|
*
|
|
26
26
|
* @example
|
|
27
27
|
* ```ts
|
|
28
|
-
* const { progress,
|
|
28
|
+
* const { progress, status, data, upload, abort } = useUploadWithProgress<{ files: Array<{ name: string }> }>()
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
* fieldName: 'files',
|
|
32
|
-
* onSuccess: (res) => console.log('上传成功', res)
|
|
33
|
-
* })
|
|
30
|
+
* await upload('/api/upload', files, { fieldName: 'files' })
|
|
34
31
|
* ```
|
|
35
32
|
*/
|
|
36
33
|
export declare function useUploadWithProgress<T = unknown>(): {
|
|
37
|
-
/** 上传进度
|
|
34
|
+
/** 上传进度 0-100 */
|
|
38
35
|
progress: any;
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
/**
|
|
36
|
+
/** 传输状态 */
|
|
37
|
+
status: any;
|
|
38
|
+
/** 解包后的业务数据 */
|
|
42
39
|
data: any;
|
|
43
40
|
/** 错误信息 */
|
|
44
41
|
error: any;
|
|
45
42
|
/** 执行上传 */
|
|
46
|
-
upload: (url:
|
|
47
|
-
data: ApiResponse<T> | null;
|
|
48
|
-
error: Error | null;
|
|
49
|
-
}>;
|
|
43
|
+
upload: (url: NitroFetchRequest, files: File | File[], options?: UploadWithProgressOptions<T>) => Promise<TransferResult<T>>;
|
|
50
44
|
/** 中止上传 */
|
|
51
45
|
abort: () => void;
|
|
52
46
|
};
|
|
@@ -1,111 +1,122 @@
|
|
|
1
|
-
import { ref, useNuxtApp } from "#imports";
|
|
1
|
+
import { ref, useNuxtApp, useRuntimeConfig } from "#imports";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "../
|
|
3
|
+
buildFetchContext,
|
|
4
|
+
finalizeTransfer,
|
|
5
|
+
parseXhrHeaders,
|
|
6
|
+
prepareTransfer,
|
|
7
|
+
tryParseJsonResponse
|
|
8
|
+
} from "../domains/api/transfer.js";
|
|
9
9
|
export function useUploadWithProgress() {
|
|
10
|
-
const
|
|
10
|
+
const publicConfig = useRuntimeConfig().public.movkApi;
|
|
11
|
+
const nuxtApp = useNuxtApp();
|
|
11
12
|
const progress = ref(0);
|
|
12
|
-
const
|
|
13
|
+
const status = ref("idle");
|
|
13
14
|
const data = ref(null);
|
|
14
15
|
const error = ref(null);
|
|
15
16
|
let currentXhr = null;
|
|
16
17
|
const abort = () => {
|
|
17
18
|
currentXhr?.abort();
|
|
18
19
|
currentXhr = null;
|
|
19
|
-
|
|
20
|
+
status.value = "aborted";
|
|
20
21
|
progress.value = 0;
|
|
21
22
|
};
|
|
22
|
-
const upload =
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const upload = (url, files, options = {}) => {
|
|
24
|
+
const {
|
|
25
|
+
fieldName = "file",
|
|
26
|
+
fields = {},
|
|
27
|
+
headers: userHeaders,
|
|
28
|
+
toast,
|
|
29
|
+
endpoint,
|
|
30
|
+
skipBusinessCheck,
|
|
31
|
+
timeoutMs,
|
|
32
|
+
withCredentials,
|
|
33
|
+
onSuccess,
|
|
34
|
+
onError
|
|
35
|
+
} = options;
|
|
36
|
+
const urlStr = typeof url === "string" ? url : url.url;
|
|
37
|
+
const { fullUrl, headers, config } = prepareTransfer(publicConfig, urlStr, {
|
|
38
|
+
endpoint,
|
|
39
|
+
headers: userHeaders,
|
|
40
|
+
toast,
|
|
41
|
+
skipBusinessCheck
|
|
42
|
+
});
|
|
27
43
|
const formData = new FormData();
|
|
28
44
|
const fileArray = Array.isArray(files) ? files : [files];
|
|
29
45
|
fileArray.forEach((file) => formData.append(fieldName, file));
|
|
30
46
|
Object.entries(fields).forEach(([key, value]) => formData.append(key, value));
|
|
31
47
|
progress.value = 0;
|
|
32
|
-
|
|
48
|
+
status.value = "pending";
|
|
33
49
|
data.value = null;
|
|
34
50
|
error.value = null;
|
|
35
51
|
return new Promise((resolve) => {
|
|
36
52
|
const xhr = new XMLHttpRequest();
|
|
37
53
|
currentXhr = xhr;
|
|
54
|
+
if (withCredentials) xhr.withCredentials = true;
|
|
55
|
+
if (timeoutMs && timeoutMs > 0) xhr.timeout = timeoutMs;
|
|
38
56
|
xhr.upload.addEventListener("progress", (e) => {
|
|
39
57
|
if (e.lengthComputable) {
|
|
40
58
|
progress.value = Math.round(e.loaded / e.total * 100);
|
|
41
59
|
}
|
|
42
60
|
});
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
const finishWith = async (raw, fallback) => {
|
|
62
|
+
const responseHeaders = parseXhrHeaders(xhr.getAllResponseHeaders());
|
|
63
|
+
const fetchContext = buildFetchContext(fullUrl, { context: { toast, skipBusinessCheck } }, {
|
|
64
|
+
status: xhr.status,
|
|
65
|
+
headers: responseHeaders
|
|
66
|
+
});
|
|
67
|
+
const result = await finalizeTransfer(nuxtApp, {
|
|
68
|
+
raw,
|
|
69
|
+
fallback: fallback ? { isSuccess: fallback.isSuccess, data: null, message: fallback.message } : void 0,
|
|
70
|
+
config,
|
|
71
|
+
publicConfig,
|
|
72
|
+
requestToast: toast,
|
|
73
|
+
skipBusinessCheck,
|
|
74
|
+
fetchContext
|
|
75
|
+
});
|
|
45
76
|
currentXhr = null;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
onError?.(err);
|
|
66
|
-
resolve({ data: null, error: err });
|
|
67
|
-
}
|
|
68
|
-
} catch (err) {
|
|
69
|
-
const parseError = err instanceof Error ? err : new Error("\u54CD\u5E94\u89E3\u6790\u5931\u8D25");
|
|
70
|
-
error.value = parseError;
|
|
71
|
-
if (import.meta.client && toast !== false) {
|
|
72
|
-
showToast("error", "\u4E0A\u4F20\u5931\u8D25", toast, config.toast);
|
|
73
|
-
}
|
|
74
|
-
onError?.(parseError);
|
|
75
|
-
resolve({ data: null, error: parseError });
|
|
77
|
+
if (result.error) {
|
|
78
|
+
status.value = "error";
|
|
79
|
+
error.value = result.error;
|
|
80
|
+
onError?.(result.error);
|
|
81
|
+
} else {
|
|
82
|
+
status.value = "success";
|
|
83
|
+
data.value = result.data;
|
|
84
|
+
progress.value = 100;
|
|
85
|
+
if (result.data !== null) onSuccess?.(result.data);
|
|
86
|
+
}
|
|
87
|
+
resolve(result);
|
|
88
|
+
};
|
|
89
|
+
xhr.addEventListener("load", () => {
|
|
90
|
+
const responseHeaders = parseXhrHeaders(xhr.getAllResponseHeaders());
|
|
91
|
+
const raw = tryParseJsonResponse(responseHeaders, xhr.responseText);
|
|
92
|
+
if (xhr.status < 200 || xhr.status >= 300) {
|
|
93
|
+
void finishWith(raw, { isSuccess: false, message: `HTTP ${xhr.status} ${xhr.statusText}` });
|
|
94
|
+
return;
|
|
76
95
|
}
|
|
96
|
+
void finishWith(raw, raw ? void 0 : { isSuccess: true, message: "\u4E0A\u4F20\u6210\u529F" });
|
|
77
97
|
});
|
|
78
98
|
xhr.addEventListener("error", () => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (import.meta.client && toast !== false) {
|
|
84
|
-
showToast("error", "\u4E0A\u4F20\u5931\u8D25", toast, config.toast);
|
|
85
|
-
}
|
|
86
|
-
onError?.(err);
|
|
87
|
-
resolve({ data: null, error: err });
|
|
99
|
+
void finishWith(null, { isSuccess: false, message: "\u7F51\u7EDC\u9519\u8BEF" });
|
|
100
|
+
});
|
|
101
|
+
xhr.addEventListener("timeout", () => {
|
|
102
|
+
void finishWith(null, { isSuccess: false, message: `\u4E0A\u4F20\u8D85\u65F6\uFF08${timeoutMs}ms\uFF09` });
|
|
88
103
|
});
|
|
89
104
|
xhr.addEventListener("abort", () => {
|
|
90
|
-
uploading.value = false;
|
|
91
105
|
currentXhr = null;
|
|
92
|
-
|
|
93
|
-
resolve({ data: null, error:
|
|
106
|
+
status.value = "aborted";
|
|
107
|
+
resolve({ data: null, error: null, aborted: true });
|
|
94
108
|
});
|
|
95
109
|
xhr.open("POST", fullUrl);
|
|
96
|
-
|
|
97
|
-
Object.entries({ ...headers, ...authHeaders }).forEach(([key, value]) => {
|
|
98
|
-
xhr.setRequestHeader(key, value);
|
|
99
|
-
});
|
|
110
|
+
Object.entries(headers).forEach(([k, v]) => xhr.setRequestHeader(k, v));
|
|
100
111
|
xhr.send(formData);
|
|
101
112
|
});
|
|
102
113
|
};
|
|
103
114
|
return {
|
|
104
|
-
/** 上传进度
|
|
115
|
+
/** 上传进度 0-100 */
|
|
105
116
|
progress,
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
/**
|
|
117
|
+
/** 传输状态 */
|
|
118
|
+
status,
|
|
119
|
+
/** 解包后的业务数据 */
|
|
109
120
|
data,
|
|
110
121
|
/** 错误信息 */
|
|
111
122
|
error,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getPath } from "@movk/core";
|
|
2
|
+
import { useNuxtApp, useUserSession } from "#imports";
|
|
3
|
+
function getUserSession() {
|
|
4
|
+
try {
|
|
5
|
+
const nuxtApp = useNuxtApp();
|
|
6
|
+
return nuxtApp.vueApp.runWithContext(() => useUserSession());
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function getTokenFromSession(tokenPath) {
|
|
12
|
+
const userSession = getUserSession();
|
|
13
|
+
if (!userSession?.session?.value) return null;
|
|
14
|
+
const sessionData = userSession.session.value;
|
|
15
|
+
return getPath(sessionData, tokenPath) || null;
|
|
16
|
+
}
|
|
17
|
+
function buildAuthHeaderValue(token, config) {
|
|
18
|
+
const tokenType = config.tokenType === "Custom" ? config.customTokenType || "" : config.tokenType || "Bearer";
|
|
19
|
+
return tokenType ? `${tokenType} ${token}` : token;
|
|
20
|
+
}
|
|
21
|
+
export function getAuthHeaders(auth) {
|
|
22
|
+
const headers = {};
|
|
23
|
+
if (!auth.enabled) return headers;
|
|
24
|
+
const tokenPath = auth.sessionTokenPath || "token";
|
|
25
|
+
const token = getTokenFromSession(tokenPath);
|
|
26
|
+
if (token) {
|
|
27
|
+
const headerName = auth.headerName || "Authorization";
|
|
28
|
+
headers[headerName] = buildAuthHeaderValue(token, auth);
|
|
29
|
+
}
|
|
30
|
+
return headers;
|
|
31
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MovkApiPublicConfig } from '../../types/api';
|
|
2
|
+
import type { EndpointPrivateConfig, ResolvedEndpointConfig } from '../../types/api/module';
|
|
3
|
+
/**
|
|
4
|
+
* 合并全局与端点配置为运行时实际生效的 ResolvedEndpointConfig
|
|
5
|
+
*
|
|
6
|
+
* @param publicConfig 模块公共配置
|
|
7
|
+
* @param endpoint 端点名称;未指定时使用 defaultEndpoint
|
|
8
|
+
* @param privateEndpoints 服务端私有端点配置(含 headers),仅在服务端可用
|
|
9
|
+
* @description 端点不存在时降级到 defaultEndpoint,并打印警告
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveEndpointConfig(publicConfig: MovkApiPublicConfig, endpoint?: string, privateEndpoints?: Record<string, EndpointPrivateConfig>): ResolvedEndpointConfig;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import defu from "defu";
|
|
2
|
+
export function resolveEndpointConfig(publicConfig, endpoint, privateEndpoints) {
|
|
3
|
+
const endpointName = endpoint || publicConfig.defaultEndpoint || "default";
|
|
4
|
+
const endpointConfig = publicConfig.endpoints?.[endpointName];
|
|
5
|
+
if (!endpointConfig) {
|
|
6
|
+
console.warn(`[@movk/nuxt] Endpoint "${endpointName}" not found, using default`);
|
|
7
|
+
return resolveEndpointConfig(publicConfig, publicConfig.defaultEndpoint || "default", privateEndpoints);
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
...endpointConfig,
|
|
11
|
+
baseURL: endpointConfig.baseURL || "",
|
|
12
|
+
headers: privateEndpoints?.[endpointName]?.headers,
|
|
13
|
+
auth: defu(endpointConfig.auth, publicConfig.auth),
|
|
14
|
+
toast: defu(endpointConfig.toast, publicConfig.toast),
|
|
15
|
+
response: defu(endpointConfig.response, publicConfig.response)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function createApiError(response, message) {
|
|
2
|
+
const error = new Error(message || "\u8BF7\u6C42\u5931\u8D25", { cause: response });
|
|
3
|
+
const rawCode = response.code ?? response.status ?? 500;
|
|
4
|
+
const numericCode = typeof rawCode === "number" ? rawCode : Number(rawCode);
|
|
5
|
+
error.statusCode = Number.isNaN(numericCode) ? 500 : numericCode;
|
|
6
|
+
error.response = response;
|
|
7
|
+
error.isBusinessError = true;
|
|
8
|
+
Error.captureStackTrace?.(error, createApiError);
|
|
9
|
+
return error;
|
|
10
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RequestToastOptions } from '../../types/api';
|
|
2
|
+
interface ApiFetchKeyInput {
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
skipUnwrap?: boolean;
|
|
5
|
+
skipBusinessCheck?: boolean;
|
|
6
|
+
toast?: RequestToastOptions | false;
|
|
7
|
+
method?: string;
|
|
8
|
+
url: unknown;
|
|
9
|
+
query?: unknown;
|
|
10
|
+
body?: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 计算 useApiFetch 的去重 key
|
|
14
|
+
*
|
|
15
|
+
* @description 纳入所有影响处理逻辑的维度(含请求级 toast),避免同 URL 不同 toast 配置
|
|
16
|
+
* 因 key 相同被 Nuxt useAsyncData 去重,复用首个 handler 导致单次配置失效。
|
|
17
|
+
* 入参须为已 resolve 的值(调用方负责 toValue)。
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildApiFetchKey(input: ApiFetchKeyInput): string;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { simpleHash } from "@movk/core";
|
|
2
|
+
export function buildApiFetchKey(input) {
|
|
3
|
+
const { endpoint, skipUnwrap, skipBusinessCheck, toast, method, url, query, body } = input;
|
|
4
|
+
const payload = {
|
|
5
|
+
e: endpoint ?? null,
|
|
6
|
+
su: skipUnwrap === true,
|
|
7
|
+
sbc: skipBusinessCheck === true,
|
|
8
|
+
t: toast ?? null,
|
|
9
|
+
m: method ?? "GET",
|
|
10
|
+
u: url,
|
|
11
|
+
q: query ?? null,
|
|
12
|
+
b: body ?? null
|
|
13
|
+
};
|
|
14
|
+
let raw;
|
|
15
|
+
try {
|
|
16
|
+
raw = JSON.stringify(payload);
|
|
17
|
+
} catch {
|
|
18
|
+
const toastTag = toast === false ? "off" : toast ? "obj" : "";
|
|
19
|
+
const urlTag = typeof url === "string" ? url : "<req>";
|
|
20
|
+
raw = `${endpoint ?? ""}|${skipUnwrap}|${skipBusinessCheck}|${toastTag}|${payload.m}|${urlTag}|<body>`;
|
|
21
|
+
}
|
|
22
|
+
return `mvk-api:${simpleHash(raw)}`;
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FetchContext } from 'ofetch';
|
|
2
|
+
import type { MovkApiPublicConfig } from '../../../types/api';
|
|
3
|
+
import type { ResolvedEndpointConfig } from '../../../types/api/module';
|
|
4
|
+
import type { useNuxtApp } from '#imports';
|
|
5
|
+
type NuxtApp = ReturnType<typeof useNuxtApp>;
|
|
6
|
+
/**
|
|
7
|
+
* 构造 onResponseError 拦截器
|
|
8
|
+
*
|
|
9
|
+
* @description 401 优先派发 `movk:api:unauthorized` hook 让业务侧自定义处理;
|
|
10
|
+
* `result.handled === true` 时**跳过** `movk:api:error` 派发,避免全局错误上报双计 401。
|
|
11
|
+
*/
|
|
12
|
+
export declare function createOnResponseError(resolvedConfig: ResolvedEndpointConfig, publicConfig: MovkApiPublicConfig, nuxtApp: NuxtApp): (context: FetchContext) => Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { navigateTo, useUserSession } from "#imports";
|
|
2
|
+
import { showToast } from "../toast.js";
|
|
3
|
+
function readApiContext(options) {
|
|
4
|
+
return options.context || {};
|
|
5
|
+
}
|
|
6
|
+
async function handleUnauthorized(authConfig, nuxtApp) {
|
|
7
|
+
const unauthorizedConfig = authConfig.unauthorized;
|
|
8
|
+
if (!unauthorizedConfig) return;
|
|
9
|
+
if (unauthorizedConfig.clearSession) {
|
|
10
|
+
try {
|
|
11
|
+
const userSession = nuxtApp.vueApp.runWithContext(() => useUserSession());
|
|
12
|
+
if (userSession?.clear) {
|
|
13
|
+
await userSession.clear();
|
|
14
|
+
await userSession.fetch();
|
|
15
|
+
}
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (unauthorizedConfig.redirect) {
|
|
20
|
+
const loginPath = unauthorizedConfig.loginPath || "/login";
|
|
21
|
+
await nuxtApp.runWithContext(() => navigateTo(loginPath));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function createOnResponseError(resolvedConfig, publicConfig, nuxtApp) {
|
|
25
|
+
const { auth: authConfig, toast: toastConfig, response: responseConfig } = resolvedConfig;
|
|
26
|
+
return async function onResponseError(context) {
|
|
27
|
+
const { response } = context;
|
|
28
|
+
if (!response) return;
|
|
29
|
+
const { toast } = readApiContext(context.options);
|
|
30
|
+
let unauthorizedHandled = false;
|
|
31
|
+
if (response.status === 401) {
|
|
32
|
+
const result = { handled: false };
|
|
33
|
+
await nuxtApp.callHook("movk:api:unauthorized", context, result);
|
|
34
|
+
unauthorizedHandled = result.handled;
|
|
35
|
+
if (!unauthorizedHandled) {
|
|
36
|
+
await handleUnauthorized(authConfig, nuxtApp);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!unauthorizedHandled) {
|
|
40
|
+
await nuxtApp.callHook("movk:api:error", context);
|
|
41
|
+
}
|
|
42
|
+
const data = response._data;
|
|
43
|
+
const fallback = `\u8BF7\u6C42\u5931\u8D25 (${response.status})`;
|
|
44
|
+
showToast("error", data ?? fallback, toast, toastConfig, responseConfig);
|
|
45
|
+
if (publicConfig.debug) {
|
|
46
|
+
console.error("[@movk/nuxt] Error:", response.status, response._data);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FetchContext } from 'ofetch';
|
|
2
|
+
import type { MovkApiPublicConfig } from '../../../types/api';
|
|
3
|
+
import type { ResolvedEndpointConfig } from '../../../types/api/module';
|
|
4
|
+
import type { useNuxtApp } from '#imports';
|
|
5
|
+
type NuxtApp = ReturnType<typeof useNuxtApp>;
|
|
6
|
+
/**
|
|
7
|
+
* 构造 onRequest 拦截器:注入认证头、debug 日志、派发 movk:api:request hook
|
|
8
|
+
*
|
|
9
|
+
* @description Headers 重建前会过滤 undefined 值,规避 ofetch 某些版本 `Record<string, string | undefined>` 导致的 TypeError
|
|
10
|
+
*/
|
|
11
|
+
export declare function createOnRequest(resolvedConfig: ResolvedEndpointConfig, publicConfig: MovkApiPublicConfig, nuxtApp: NuxtApp): (context: FetchContext) => Promise<void>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getAuthHeaders } from "../auth.js";
|
|
2
|
+
function toHeaderRecord(headers) {
|
|
3
|
+
const out = {};
|
|
4
|
+
if (!headers) return out;
|
|
5
|
+
if (headers instanceof Headers) {
|
|
6
|
+
headers.forEach((value, key) => {
|
|
7
|
+
out[key] = value;
|
|
8
|
+
});
|
|
9
|
+
return out;
|
|
10
|
+
}
|
|
11
|
+
if (Array.isArray(headers)) {
|
|
12
|
+
for (const entry of headers) {
|
|
13
|
+
if (entry && entry[1] !== void 0) out[entry[0]] = entry[1];
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
18
|
+
if (value !== void 0) out[key] = value;
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
export function createOnRequest(resolvedConfig, publicConfig, nuxtApp) {
|
|
23
|
+
const { auth: authConfig } = resolvedConfig;
|
|
24
|
+
return async function onRequest(context) {
|
|
25
|
+
if (authConfig.enabled) {
|
|
26
|
+
const authHeaders = getAuthHeaders(authConfig);
|
|
27
|
+
const normalized = toHeaderRecord(context.options.headers);
|
|
28
|
+
const headers = new Headers(normalized);
|
|
29
|
+
for (const [key, value] of Object.entries(authHeaders)) {
|
|
30
|
+
headers.set(key, value);
|
|
31
|
+
}
|
|
32
|
+
context.options.headers = headers;
|
|
33
|
+
}
|
|
34
|
+
if (publicConfig.debug) {
|
|
35
|
+
const h = context.options.headers;
|
|
36
|
+
const headersLog = h instanceof Headers ? Object.fromEntries(h.entries()) : h;
|
|
37
|
+
console.info("[@movk/nuxt] Request:", {
|
|
38
|
+
method: context.options.method || "GET",
|
|
39
|
+
url: `${resolvedConfig.baseURL}${context.request}`,
|
|
40
|
+
headers: headersLog,
|
|
41
|
+
body: context.options.body ? "(body present)" : void 0
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
await nuxtApp.callHook("movk:api:request", context);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FetchContext } from 'ofetch';
|
|
2
|
+
import type { MovkApiPublicConfig } from '../../../types/api';
|
|
3
|
+
import type { ResolvedEndpointConfig } from '../../../types/api/module';
|
|
4
|
+
import type { useNuxtApp } from '#imports';
|
|
5
|
+
type NuxtApp = ReturnType<typeof useNuxtApp>;
|
|
6
|
+
/**
|
|
7
|
+
* 构造 onResponse 拦截器
|
|
8
|
+
*
|
|
9
|
+
* @description 业务校验链路:
|
|
10
|
+
* - 校验通过(或 skipBusinessCheck)→ 按 skipUnwrap 决定是否解包 _data → 派发 movk:api:response hook → 触发成功 toast
|
|
11
|
+
* - 校验失败 → 派发 movk:api:error hook → 触发错误 toast → 抛 createApiError
|
|
12
|
+
*
|
|
13
|
+
* **契约**:拦截器输出 = 业务数据。`movk:api:response` hook 收到的 `context.response._data` 已解包;
|
|
14
|
+
* 调用方设置 `skipUnwrap: true` 时不重写 `_data`,保留原始响应。
|
|
15
|
+
*/
|
|
16
|
+
export declare function createOnResponse(resolvedConfig: ResolvedEndpointConfig, publicConfig: MovkApiPublicConfig, nuxtApp: NuxtApp): (context: FetchContext) => Promise<void>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { extractData, extractMessage, isBusinessSuccess } from "../response.js";
|
|
2
|
+
import { createApiError } from "../errors.js";
|
|
3
|
+
import { showToast } from "../toast.js";
|
|
4
|
+
function readApiContext(options) {
|
|
5
|
+
return options.context || {};
|
|
6
|
+
}
|
|
7
|
+
export function createOnResponse(resolvedConfig, publicConfig, nuxtApp) {
|
|
8
|
+
const { toast: toastConfig, response: responseConfig } = resolvedConfig;
|
|
9
|
+
return async function onResponse(context) {
|
|
10
|
+
const raw = context.response?._data;
|
|
11
|
+
const { toast, skipBusinessCheck, skipUnwrap } = readApiContext(context.options);
|
|
12
|
+
if (publicConfig.debug) {
|
|
13
|
+
console.info("[@movk/nuxt] Response:", raw);
|
|
14
|
+
}
|
|
15
|
+
if (skipBusinessCheck || isBusinessSuccess(raw, responseConfig)) {
|
|
16
|
+
if (!skipUnwrap && context.response) {
|
|
17
|
+
context.response._data = extractData(raw, responseConfig);
|
|
18
|
+
}
|
|
19
|
+
await nuxtApp.callHook("movk:api:response", context);
|
|
20
|
+
showToast("success", raw, toast, toastConfig, responseConfig);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
await nuxtApp.callHook("movk:api:error", context);
|
|
24
|
+
showToast("error", raw, toast, toastConfig, responseConfig);
|
|
25
|
+
throw createApiError(raw, extractMessage(raw, responseConfig));
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ApiResponse, ApiResponseConfig } from '../../types/api';
|
|
2
|
+
export declare function isBusinessSuccess(response: ApiResponse, config?: Partial<ApiResponseConfig>): boolean;
|
|
3
|
+
export declare function extractMessage(response: ApiResponse, config?: Partial<ApiResponseConfig>): string | undefined;
|
|
4
|
+
export declare function extractData(response: ApiResponse, config?: Partial<ApiResponseConfig>): unknown;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function isBusinessSuccess(response, config = {}) {
|
|
2
|
+
const codeKey = config.codeKey || "code";
|
|
3
|
+
const successCodes = config.successCodes || [200, 0];
|
|
4
|
+
const code = response[codeKey];
|
|
5
|
+
return successCodes.includes(code);
|
|
6
|
+
}
|
|
7
|
+
export function extractMessage(response, config = {}) {
|
|
8
|
+
const messageKey = config.messageKey || "message";
|
|
9
|
+
return response[messageKey] || response.message || response.msg;
|
|
10
|
+
}
|
|
11
|
+
export function extractData(response, config = {}) {
|
|
12
|
+
const dataKey = config.dataKey || "data";
|
|
13
|
+
return dataKey in response ? response[dataKey] : void 0;
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ApiResponse, ApiResponseConfig, ApiToastConfig, RequestToastOptions } from '../../types/api';
|
|
2
|
+
type ToastType = 'success' | 'error';
|
|
3
|
+
/**
|
|
4
|
+
* 触发 Toast 提示
|
|
5
|
+
*
|
|
6
|
+
* @description 决策收敛在本函数内:是否启用 / 类型是否启用 / 提示文案选择 / payload 构造。
|
|
7
|
+
*
|
|
8
|
+
* @param type 提示类型
|
|
9
|
+
* @param source 字符串作为 fallback 文案;ApiResponse 时按 responseConfig 抽取 message;undefined 走类型默认文案
|
|
10
|
+
* @param requestOptions 单次请求覆盖配置;为 false 时整体禁用
|
|
11
|
+
* @param globalConfig 全局 Toast 配置(已合并端点级覆盖)
|
|
12
|
+
* @param responseConfig 用于从 ApiResponse 抽取 message 的字段映射;source 为字符串时可省略
|
|
13
|
+
*/
|
|
14
|
+
export declare function showToast(type: ToastType, source: ApiResponse | string | undefined, requestOptions: RequestToastOptions | false | undefined, globalConfig: Partial<ApiToastConfig>, responseConfig?: Partial<ApiResponseConfig>): void;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { extractMessage } from "./response.js";
|
|
2
|
+
import { useNuxtApp, useToast } from "#imports";
|
|
3
|
+
function getToast() {
|
|
4
|
+
try {
|
|
5
|
+
const nuxtApp = useNuxtApp();
|
|
6
|
+
return nuxtApp.vueApp.runWithContext(() => useToast());
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function pickMessage(type, source, requestOptions, responseConfig) {
|
|
12
|
+
if (typeof requestOptions === "object") {
|
|
13
|
+
const custom = requestOptions[`${type}Message`];
|
|
14
|
+
if (custom) return custom;
|
|
15
|
+
}
|
|
16
|
+
if (typeof source === "string") return source;
|
|
17
|
+
if (source && responseConfig) {
|
|
18
|
+
const extracted = extractMessage(source, responseConfig);
|
|
19
|
+
if (extracted) return extracted;
|
|
20
|
+
}
|
|
21
|
+
return type === "success" ? "\u64CD\u4F5C\u6210\u529F" : "\u8BF7\u6C42\u5931\u8D25";
|
|
22
|
+
}
|
|
23
|
+
function compact(obj) {
|
|
24
|
+
const out = {};
|
|
25
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
26
|
+
if (v !== void 0) out[k] = v;
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
export function showToast(type, source, requestOptions, globalConfig, responseConfig) {
|
|
31
|
+
if (globalConfig.enabled === false || requestOptions === false) return;
|
|
32
|
+
const typeConfig = globalConfig[type];
|
|
33
|
+
if (typeConfig?.show === false) return;
|
|
34
|
+
if (requestOptions?.[type] === false) return;
|
|
35
|
+
const message = pickMessage(type, source, requestOptions, responseConfig);
|
|
36
|
+
if (!message) return;
|
|
37
|
+
const toast = getToast();
|
|
38
|
+
if (!toast) return;
|
|
39
|
+
const { show: _show, ...typeConfigProps } = typeConfig ?? {};
|
|
40
|
+
const requestTypeConfig = typeof requestOptions?.[type] === "object" ? requestOptions[type] : {};
|
|
41
|
+
toast.add(compact({
|
|
42
|
+
title: message,
|
|
43
|
+
...typeConfigProps,
|
|
44
|
+
...requestTypeConfig
|
|
45
|
+
}));
|
|
46
|
+
}
|