From df655015b1228a1d312fbc1a7fdd73293622eda3 Mon Sep 17 00:00:00 2001 From: lighua <877312980@qq.com> Date: Fri, 8 Aug 2025 15:31:31 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E5=99=A8=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../effects/request/src/request-client/modules/downloader.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/effects/request/src/request-client/modules/downloader.ts b/packages/effects/request/src/request-client/modules/downloader.ts index 77e72c6c..f5bd101a 100644 --- a/packages/effects/request/src/request-client/modules/downloader.ts +++ b/packages/effects/request/src/request-client/modules/downloader.ts @@ -28,11 +28,12 @@ class FileDownloader { ): Promise { const finalConfig: DownloadRequestConfig = { responseReturn: 'body', + method: 'GET', ...config, responseType: 'blob', }; - const response = await this.client.get(url, finalConfig); + const response = await this.client.request(url, finalConfig); return response; } From fddfc6d4940aa0a7332b5ff3a33a3fcabf5092eb Mon Sep 17 00:00:00 2001 From: lighua <877312980@qq.com> Date: Thu, 21 Aug 2025 11:13:02 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0=E5=AF=B9=E4=B8=8D?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=9A=84=20HTTP=20=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request-client/modules/downloader.test.ts | 71 +++++++++++++++++++ .../src/request-client/modules/downloader.ts | 22 +++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/packages/effects/request/src/request-client/modules/downloader.test.ts b/packages/effects/request/src/request-client/modules/downloader.test.ts index d44dcbb7..27e8a30c 100644 --- a/packages/effects/request/src/request-client/modules/downloader.test.ts +++ b/packages/effects/request/src/request-client/modules/downloader.test.ts @@ -30,6 +30,7 @@ describe('fileDownloader', () => { expect(result).toBeInstanceOf(Blob); expect(result).toEqual(mockBlob); expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { + method: 'GET', responseType: 'blob', responseReturn: 'body', }); @@ -51,6 +52,7 @@ describe('fileDownloader', () => { expect(result).toEqual(mockBlob); expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { ...customConfig, + method: 'GET', responseType: 'blob', responseReturn: 'body', }); @@ -84,3 +86,72 @@ describe('fileDownloader', () => { ); }); }); + +describe('fileDownloader use other method', () => { + let fileDownloader: FileDownloader; + + it('should call request using get', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + const mockAxiosInstance = { + request: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + + mockAxiosInstance.request.mockResolvedValueOnce(mockResponse); + + const result = await fileDownloader.download(url); + + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.request).toHaveBeenCalledWith(url, { + method: 'GET', + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should call post', async () => { + const url = 'https://example.com/file'; + + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + + const customConfig: AxiosRequestConfig = { + method: 'POST', + data: { name: 'aa' }, + }; + + await fileDownloader.download(url, customConfig); + + expect(mockAxiosInstance.post).toHaveBeenCalledWith( + url, + { name: 'aa' }, + { + method: 'POST', + responseType: 'blob', + responseReturn: 'body', + }, + ); + }); + + it('should handle errors gracefully', async () => { + const url = 'https://example.com/file'; + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + await expect(() => + fileDownloader.download(url, { method: 'postt' }), + ).rejects.toThrow( + 'RequestClient does not support method "POSTT". Please ensure the method is properly implemented in your RequestClient instance.', + ); + }); +}); diff --git a/packages/effects/request/src/request-client/modules/downloader.ts b/packages/effects/request/src/request-client/modules/downloader.ts index f5bd101a..6c300058 100644 --- a/packages/effects/request/src/request-client/modules/downloader.ts +++ b/packages/effects/request/src/request-client/modules/downloader.ts @@ -33,9 +33,27 @@ class FileDownloader { responseType: 'blob', }; - const response = await this.client.request(url, finalConfig); + // Prefer a generic request if available; otherwise, dispatch to method-specific calls. + const method = (finalConfig.method || 'GET').toUpperCase(); + const clientAny = this.client as any; - return response; + if (typeof clientAny.request === 'function') { + return await clientAny.request(url, finalConfig); + } + const lower = method.toLowerCase(); + + if (typeof clientAny[lower] === 'function') { + if (['POST', 'PUT'].includes(method)) { + const { data, ...rest } = finalConfig as Record; + return await clientAny[lower](url, data, rest); + } + + return await clientAny[lower](url, finalConfig); + } + + throw new Error( + `RequestClient does not support method "${method}". Please ensure the method is properly implemented in your RequestClient instance.`, + ); } } From 573637222d9167ef50bf4eb4fb7d8ffe4b983ea3 Mon Sep 17 00:00:00 2001 From: panda7 Date: Wed, 12 Nov 2025 02:03:12 +0800 Subject: [PATCH 3/3] feat: add form handleCollapsedChange event (#6893) * feat: add form handleCollapsedChange event * fix: ts lint * fix: ts error --------- Co-authored-by: sqchen --- docs/src/components/common-ui/vben-form.md | 1 + .../form-ui/src/components/form-actions.vue | 4 ++-- packages/@core/ui-kit/form-ui/src/form-api.ts | 1 + packages/@core/ui-kit/form-ui/src/types.ts | 4 ++++ .../ui-kit/form-ui/src/use-form-context.ts | 2 +- .../@core/ui-kit/form-ui/src/vben-form.vue | 4 +++- .../ui-kit/form-ui/src/vben-use-form.vue | 21 +++++++++++-------- 7 files changed, 24 insertions(+), 13 deletions(-) diff --git a/docs/src/components/common-ui/vben-form.md b/docs/src/components/common-ui/vben-form.md index 48772bfa..30cbec44 100644 --- a/docs/src/components/common-ui/vben-form.md +++ b/docs/src/components/common-ui/vben-form.md @@ -335,6 +335,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单 | handleReset | 表单重置回调 | `(values: Record,) => Promise \| void` | - | | handleSubmit | 表单提交回调 | `(values: Record,) => Promise \| void` | - | | handleValuesChange | 表单值变化回调 | `(values: Record, fieldsChanged: string[]) => void` | - | +| handleCollapsedChange | 表单收起展开状态变化回调 | `(collapsed: boolean) => void` | - | | actionButtonsReverse | 调换操作按钮位置 | `boolean` | `false` | | resetButtonOptions | 重置按钮组件参数 | `ActionButtonOptions` | - | | submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - | diff --git a/packages/@core/ui-kit/form-ui/src/components/form-actions.vue b/packages/@core/ui-kit/form-ui/src/components/form-actions.vue index 42101c12..36b1caa0 100644 --- a/packages/@core/ui-kit/form-ui/src/components/form-actions.vue +++ b/packages/@core/ui-kit/form-ui/src/components/form-actions.vue @@ -47,7 +47,7 @@ async function handleSubmit(e: Event) { return; } - const values = toRaw(await props.formApi.getValues()); + const values = toRaw(await props.formApi.getValues()) ?? {}; await props.handleSubmit?.(values); } @@ -56,7 +56,7 @@ async function handleReset(e: Event) { e?.stopPropagation(); const props = unref(rootProps); - const values = toRaw(await props.formApi?.getValues()); + const values = toRaw(await props.formApi?.getValues()) ?? {}; if (isFunction(props.handleReset)) { await props.handleReset?.(values); diff --git a/packages/@core/ui-kit/form-ui/src/form-api.ts b/packages/@core/ui-kit/form-ui/src/form-api.ts index 401748c3..f5f353dc 100644 --- a/packages/@core/ui-kit/form-ui/src/form-api.ts +++ b/packages/@core/ui-kit/form-ui/src/form-api.ts @@ -36,6 +36,7 @@ function getDefaultState(): VbenFormProps { handleReset: undefined, handleSubmit: undefined, handleValuesChange: undefined, + handleCollapsedChange: undefined, layout: 'horizontal', resetButtonOptions: {}, schema: [], diff --git a/packages/@core/ui-kit/form-ui/src/types.ts b/packages/@core/ui-kit/form-ui/src/types.ts index 824b7a44..6d704145 100644 --- a/packages/@core/ui-kit/form-ui/src/types.ts +++ b/packages/@core/ui-kit/form-ui/src/types.ts @@ -379,6 +379,10 @@ export interface VbenFormProps< * 表单字段映射 */ fieldMappingTime?: FieldMappingTime; + /** + * 表单收起展开状态变化回调 + */ + handleCollapsedChange?: (collapsed: boolean) => void; /** * 表单重置回调 */ diff --git a/packages/@core/ui-kit/form-ui/src/use-form-context.ts b/packages/@core/ui-kit/form-ui/src/use-form-context.ts index 4ef182ed..e95c87b9 100644 --- a/packages/@core/ui-kit/form-ui/src/use-form-context.ts +++ b/packages/@core/ui-kit/form-ui/src/use-form-context.ts @@ -13,7 +13,7 @@ import { useForm } from 'vee-validate'; import { object, ZodIntersection, ZodNumber, ZodObject, ZodString } from 'zod'; import { getDefaultsForSchema } from 'zod-defaults'; -type ExtendFormProps = VbenFormProps & { formApi: ExtendedFormApi }; +type ExtendFormProps = VbenFormProps & { formApi?: ExtendedFormApi }; export const [injectFormProps, provideFormProps] = createContext<[ComputedRef | ExtendFormProps, FormActions]>( diff --git a/packages/@core/ui-kit/form-ui/src/vben-form.vue b/packages/@core/ui-kit/form-ui/src/vben-form.vue index 260e75cd..f3ba37f0 100644 --- a/packages/@core/ui-kit/form-ui/src/vben-form.vue +++ b/packages/@core/ui-kit/form-ui/src/vben-form.vue @@ -40,7 +40,9 @@ const { delegatedSlots, form } = useFormInitial(props); provideFormProps([props, form]); const handleUpdateCollapsed = (value: boolean) => { - currentCollapsed.value = !!value; + currentCollapsed.value = value; + // 触发收起展开状态变化回调 + props.handleCollapsedChange?.(value); }; watchEffect(() => { diff --git a/packages/@core/ui-kit/form-ui/src/vben-use-form.vue b/packages/@core/ui-kit/form-ui/src/vben-use-form.vue index 3e7b00b6..e9386725 100644 --- a/packages/@core/ui-kit/form-ui/src/vben-use-form.vue +++ b/packages/@core/ui-kit/form-ui/src/vben-use-form.vue @@ -25,7 +25,7 @@ import { } from './use-form-context'; // 通过 extends 会导致热更新卡死,所以重复写了一遍 interface Props extends VbenFormProps { - formApi: ExtendedFormApi; + formApi?: ExtendedFormApi; } const props = defineProps(); @@ -44,11 +44,13 @@ provideComponentRefMap(componentRefMap); props.formApi?.mount?.(form, componentRefMap); const handleUpdateCollapsed = (value: boolean) => { - props.formApi?.setState({ collapsed: !!value }); + props.formApi?.setState({ collapsed: value }); + // 触发收起展开状态变化回调 + forward.value.handleCollapsedChange?.(value); }; function handleKeyDownEnter(event: KeyboardEvent) { - if (!state.value.submitOnEnter || !forward.value.formApi?.isMounted) { + if (!state?.value.submitOnEnter || !forward.value.formApi?.isMounted) { return; } // 如果是 textarea 不阻止默认行为,否则会导致无法换行。 @@ -58,11 +60,11 @@ function handleKeyDownEnter(event: KeyboardEvent) { } event.preventDefault(); - forward.value.formApi.validateAndSubmitForm(); + forward.value.formApi?.validateAndSubmitForm(); } const handleValuesChangeDebounced = useDebounceFn(async () => { - state.value.submitOnChange && forward.value.formApi?.validateAndSubmitForm(); + state?.value.submitOnChange && forward.value.formApi?.validateAndSubmitForm(); }, 300); const valuesCache: Recordable = {}; @@ -74,7 +76,7 @@ onMounted(async () => { () => form.values, async (newVal) => { if (forward.value.handleValuesChange) { - const fields = state.value.schema?.map((item) => { + const fields = state?.value.schema?.map((item) => { return item.fieldName; }); @@ -91,8 +93,9 @@ onMounted(async () => { if (changedFields.length > 0) { // 调用handleValuesChange回调,传入所有表单值的深拷贝和变更的字段列表 + const values = await forward.value.formApi?.getValues(); forward.value.handleValuesChange( - cloneDeep(await forward.value.formApi.getValues()), + cloneDeep(values ?? {}) as Record, changedFields, ); } @@ -109,7 +112,7 @@ onMounted(async () => {
{