diff --git a/docs/src/components/common-ui/vben-form.md b/docs/src/components/common-ui/vben-form.md index a18ee42d..f9823091 100644 --- a/docs/src/components/common-ui/vben-form.md +++ b/docs/src/components/common-ui/vben-form.md @@ -323,6 +323,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单 | schema | 表单项的每一项配置 | `FormSchema[]` | - | | submitOnEnter | 按下回车健时提交表单 | `boolean` | false | | submitOnChange | 字段值改变时提交表单(内部防抖,这个属性一般用于表格的搜索表单) | `boolean` | false | +| compact | 是否紧凑模式(忽略为校验信息所预留的空间) | `boolean` | false | ::: tip fieldMappingTime @@ -365,13 +366,6 @@ export interface FormCommonConfig { * 所有表单项的props */ componentProps?: ComponentProps; - /** - * 是否紧凑模式(移除表单底部为显示校验错误信息所预留的空间)。 - * 在有设置校验规则的场景下,建议不要将其设置为true - * 默认为false。但用作表格的搜索表单时,默认为true - * @default false - */ - compact?: boolean; /** * 所有表单项的控件样式 */ diff --git a/docs/src/guide/essentials/server.md b/docs/src/guide/essentials/server.md index fedfbae2..9a494967 100644 --- a/docs/src/guide/essentials/server.md +++ b/docs/src/guide/essentials/server.md @@ -110,6 +110,36 @@ VITE_GLOB_API_URL=https://mock-napi.vben.pro/api 项目中默认自带了基于 `axios` 封装的基础的请求配置,核心由 `@vben/request` 包提供。项目没有过多的封装,只是简单的封装了一些常用的配置,如有其他需求,可以自行增加或者调整配置。针对不同的app,可能是用到了不同的组件库以及`store`,所以在应用目录下的`src/api/request.ts`文件夹下,有对应的请求配置文件,如`web-antd`项目下的`src/api/request.ts`文件,可以根据自己的需求进行配置。 +### 扩展的配置 + +除了基础的Axios配置外,扩展了部分配置。 + +```ts +type ExtendOptions = { + /** + * 参数序列化方式。预置了几种针对数组的序列化类型 + * - brackets: ids[]=1&ids[]=2&ids[]=3 + * - comma: ids=1,2,3 + * - indices: ids[0]=1&ids[1]=2&ids[2]=3 + * - repeat: ids=1&ids=2&ids=3 + * @default 'brackets' + */ + paramsSerializer?: + | 'brackets' + | 'comma' + | 'indices' + | 'repeat' + | AxiosRequestConfig['paramsSerializer']; + /** + * 响应数据的返回方式。 + * - raw: 原始的AxiosResponse,包括headers、status等,不做是否成功请求的检查。 + * - body: 返回响应数据的BODY部分(只会根据status检查请求是否成功,忽略对code的判断,这种情况下应由调用方检查请求是否成功)。 + * - data: 解构响应的BODY数据,只返回其中的data节点数据(会检查status和code是否为成功状态)。 + */ + responseReturn?: 'body' | 'data' | 'raw'; +}; +``` + ### 请求示例 #### GET 请求 diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue index b75088bb..295eff95 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue @@ -210,7 +210,7 @@ defineExpose({ v-slot="{ flattenItems }" :class=" cn( - 'text-blackA11 select-none list-none rounded-lg p-2 text-sm font-medium', + 'text-blackA11 container select-none list-none rounded-lg p-2 text-sm font-medium', $attrs.class as unknown as ClassType, bordered ? 'border' : '', ) @@ -219,11 +219,7 @@ defineExpose({
- + + qs.stringify(params, { arrayFormat: 'brackets' }); + } + case 'comma': { + return (params: any) => qs.stringify(params, { arrayFormat: 'comma' }); + } + case 'indices': { + return (params: any) => + qs.stringify(params, { arrayFormat: 'indices' }); + } + case 'repeat': { + return (params: any) => qs.stringify(params, { arrayFormat: 'repeat' }); + } + } + } + return paramsSerializer; +} + class RequestClient { public addRequestInterceptor: InterceptorManager['addRequestInterceptor']; @@ -39,6 +64,9 @@ class RequestClient { }; const { ...axiosConfig } = options; const requestConfig = merge(axiosConfig, defaultConfig); + requestConfig.paramsSerializer = getParamsSerializer( + requestConfig.paramsSerializer, + ); this.instance = axios.create(requestConfig); bindMethods(this); @@ -154,6 +182,9 @@ class RequestClient { const response: AxiosResponse = await this.instance({ url, ...config, + ...(config.paramsSerializer + ? { paramsSerializer: getParamsSerializer(config.paramsSerializer) } + : {}), }); return response as T; } catch (error: any) { diff --git a/packages/effects/request/src/request-client/types.ts b/packages/effects/request/src/request-client/types.ts index 5f5b49ec..62e8eceb 100644 --- a/packages/effects/request/src/request-client/types.ts +++ b/packages/effects/request/src/request-client/types.ts @@ -5,15 +5,29 @@ import type { InternalAxiosRequestConfig, } from 'axios'; -type ExtendOptions = { - /** 响应数据的返回方式。 - * raw: 原始的AxiosResponse,包括headers、status等,不做是否成功请求的检查。 - * body: 返回响应数据的BODY部分(只会根据status检查请求是否成功,忽略对code的判断,这种情况下应由调用方检查请求是否成功)。 - * data: 解构响应的BODY数据,只返回其中的data节点数据(会检查status和code是否为成功状态)。 +type ExtendOptions = { + /** + * 参数序列化方式。预置的有 + * - brackets: ids[]=1&ids[]=2&ids[]=3 + * - comma: ids=1,2,3 + * - indices: ids[0]=1&ids[1]=2&ids[2]=3 + * - repeat: ids=1&ids=2&ids=3 + */ + paramsSerializer?: + | 'brackets' + | 'comma' + | 'indices' + | 'repeat' + | AxiosRequestConfig['paramsSerializer']; + /** + * 响应数据的返回方式。 + * - raw: 原始的AxiosResponse,包括headers、status等,不做是否成功请求的检查。 + * - body: 返回响应数据的BODY部分(只会根据status检查请求是否成功,忽略对code的判断,这种情况下应由调用方检查请求是否成功)。 + * - data: 解构响应的BODY数据,只返回其中的data节点数据(会检查status和code是否为成功状态)。 */ responseReturn?: 'body' | 'data' | 'raw'; }; -type RequestClientConfig = AxiosRequestConfig & ExtendOptions; +type RequestClientConfig = AxiosRequestConfig & ExtendOptions; type RequestResponse = AxiosResponse & { config: RequestClientConfig; diff --git a/packages/icons/src/index.ts b/packages/icons/src/index.ts index 53d7b008..4ab6a863 100644 --- a/packages/icons/src/index.ts +++ b/packages/icons/src/index.ts @@ -1,5 +1,5 @@ -export * from './iconify/index.js'; -export * from './iconify-offline/index.js'; +export * from './iconify'; +export * from './iconify-offline'; export { default as EmptyIcon } from './icons/empty-icon.vue'; -export * from './svg/index.js'; +export * from './svg'; export { VbenIcon } from '@vben-core/shadcn-ui'; diff --git a/playground/src/api/examples/params.ts b/playground/src/api/examples/params.ts new file mode 100644 index 00000000..6568ec64 --- /dev/null +++ b/playground/src/api/examples/params.ts @@ -0,0 +1,19 @@ +import type { Recordable } from '@vben/types'; + +import { requestClient } from '#/api/request'; + +/** + * 发起数组请求 + */ +async function getParamsData( + params: Recordable, + type: 'brackets' | 'comma' | 'indices' | 'repeat', +) { + return requestClient.get('/status', { + params, + paramsSerializer: type, + responseReturn: 'raw', + }); +} + +export { getParamsData }; diff --git a/playground/src/locales/langs/zh-CN/demos.json b/playground/src/locales/langs/zh-CN/demos.json index 254e072b..5cd87ce5 100644 --- a/playground/src/locales/langs/zh-CN/demos.json +++ b/playground/src/locales/langs/zh-CN/demos.json @@ -50,7 +50,8 @@ "clipboard": "剪贴板", "menuWithQuery": "带参菜单", "openInNewWindow": "新窗口打开", - "fileDownload": "文件下载" + "fileDownload": "文件下载", + "requestParamsSerializer": "参数序列化" }, "breadcrumb": { "navigation": "面包屑导航", diff --git a/playground/src/router/routes/modules/demos.ts b/playground/src/router/routes/modules/demos.ts index 955bfe92..7b927373 100644 --- a/playground/src/router/routes/modules/demos.ts +++ b/playground/src/router/routes/modules/demos.ts @@ -243,6 +243,18 @@ const routes: RouteRecordRaw[] = [ title: 'Tanstack Query', }, }, + { + name: 'RequestParamsSerializerDemo', + path: '/demos/features/request-params-serializer', + component: () => + import( + '#/views/demos/features/request-params-serializer/index.vue' + ), + meta: { + icon: 'lucide:git-pull-request-arrow', + title: $t('demos.features.requestParamsSerializer'), + }, + }, ], }, // 面包屑导航 diff --git a/playground/src/views/demos/features/request-params-serializer/index.vue b/playground/src/views/demos/features/request-params-serializer/index.vue new file mode 100644 index 00000000..4ed4d08e --- /dev/null +++ b/playground/src/views/demos/features/request-params-serializer/index.vue @@ -0,0 +1,61 @@ + + diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f7f77cfd..604cdf3a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -50,6 +50,7 @@ catalog: '@types/nprogress': ^0.2.3 '@types/postcss-import': ^14.0.3 '@types/qrcode': ^1.5.5 + '@types/qs': ^6.9.18 '@types/sortablejs': ^1.15.8 '@typescript-eslint/eslint-plugin': ^8.26.0 '@typescript-eslint/parser': ^8.26.0 @@ -139,6 +140,7 @@ catalog: prettier-plugin-tailwindcss: ^0.6.11 publint: ^0.2.12 qrcode: ^1.5.4 + qs: ^6.14.0 radix-vue: ^1.9.17 resolve.exports: ^2.0.3 rimraf: ^6.0.1