Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions docs/api/functions/generateUrlWithQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

> **generateUrlWithQuery**(`pathname`, `queryParams`): `string`

Defined in: [generateUrlWithQuery/index.ts:32](https://github.com/DTStack/dt-utils/blob/master/src/generateUrlWithQuery/index.ts#L32)
Defined in: [generateUrlWithQuery/index.ts:44](https://github.com/DTStack/dt-utils/blob/master/src/generateUrlWithQuery/index.ts#L44)

生成带查询参数的 URL

Expand Down Expand Up @@ -42,8 +42,20 @@ import { generateUrlWithQuery } from 'dt-utils';
generateUrlWithQuery('/api/users', { id: 123 }) // => '/api/users?id=123'

// 多个参数
generateUrlWithQuery('/search', { q: 'test', page: 1, sort: 'desc' }) // => '/search?q=test&page=1&sort=desc'
generateUrlWithQuery('/search', { q: 'test', page: 1, sort: 'desc', tags: ['active', 'user'] }) // => '/search?q=test&page=1&sort=desc&tags=active,user'

// 处理无效值
generateUrlWithQuery('/api/data', { id: 123, name: null, status: undefined }) // => '/api/data?id=123'
generateUrlWithQuery('/api/data', { id: 123, name: null, status: undefined, empty: '', emptyArray: [] }) // => '/api/data?id=123'

// 数组类型参数
generateUrlWithQuery('/api/users', { ids: [1, 2, 3], tags: ['a', 'b'] }) // => '/api/users?ids=1,2,3&tags=a,b'

// 嵌套数组(会被扁平化)
generateUrlWithQuery('/api/data', { matrix: [[1, 2], [3, 4]] }) // => '/api/data?matrix=1,2,3,4'

// 混合类型数组
generateUrlWithQuery('/api/data', { mixed: [1, 'two', true, null, undefined] }) // => '/api/data?mixed=1,two,true,,'

// 复杂类型数组(使用 toString() 转换)
generateUrlWithQuery('/api/data', { objects: [{ a: 1 }, { b: 2 }] }) // => '/api/data?objects=[object Object],[object Object]'
```
72 changes: 72 additions & 0 deletions src/generateUrlWithQuery/__test__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,76 @@ describe('generateUrlWithQuery', () => {
generateUrlWithQuery('/api/users', { obj: circularObj } as Record<string, any>)
).toBe('/api/users');
});

test('handles array type parameters', () => {
const params = {
ids: [1, 2, 3],
tags: ['a', 'b'],
Comment thread
jin-sir marked this conversation as resolved.
} as Record<string, any>;
expect(generateUrlWithQuery('/api/users', params)).toBe(
'/api/users?ids=1%2C2%2C3&tags=a%2Cb'
);
});

test('handles empty array', () => {
const params = {
ids: [],
name: 'test',
} as Record<string, any>;
// Empty array converts to empty string, which gets filtered out
expect(generateUrlWithQuery('/api/users', params)).toBe('/api/users?name=test');
});

test('handles nested array', () => {
const params = {
matrix: [
[1, 2],
[3, 4],
],
tags: [
['a', 'b'],
['c', 'd'],
],
} as Record<string, any>;
expect(generateUrlWithQuery('/api/data', params)).toBe(
'/api/data?matrix=1%2C2%2C3%2C4&tags=a%2Cb%2Cc%2Cd'
);
});

test('handles mixed type array', () => {
const params = {
mixed: [1, 'two', true, null, undefined],
numbers: [1, 2.5, -3],
} as Record<string, any>;
expect(generateUrlWithQuery('/api/data', params)).toBe(
'/api/data?mixed=1%2Ctwo%2Ctrue%2C%2C&numbers=1%2C2.5%2C-3'
);
});

test('handles complex type array', () => {
// Create objects with custom toString for predictable output
class CustomObj {
value: number;
constructor(value: number) {
this.value = value;
}
toString() {
return `Custom(${this.value})`;
}
}

const params = {
objects: [new CustomObj(1), new CustomObj(2)],
simpleObjects: [{ a: 1 }, { b: 2 }],
} as Record<string, any>;

// Expected URL encoding:
// Custom(1) -> Custom%281%29
// Custom(2) -> Custom%282%29
// comma -> %2C
// [object Object] -> %5Bobject+Object%5D
expect(generateUrlWithQuery('/api/data', params)).toBe(
'/api/data?objects=Custom%281%29%2CCustom%282%29&simpleObjects=%5Bobject+Object%5D%2C%5Bobject+Object%5D'
);
});
});
23 changes: 18 additions & 5 deletions src/generateUrlWithQuery/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import getTypeOfValue from '../getTypeOfValue';

type QueryValue = string | number | boolean | null | undefined;
type QueryValue = string | number | boolean | any[] | null | undefined;
type QueryParams = Record<string, QueryValue>;

/**
Expand All @@ -23,10 +23,22 @@ type QueryParams = Record<string, QueryValue>;
* generateUrlWithQuery('/api/users', { id: 123 }) // => '/api/users?id=123'
*
* // 多个参数
* generateUrlWithQuery('/search', { q: 'test', page: 1, sort: 'desc' }) // => '/search?q=test&page=1&sort=desc'
* generateUrlWithQuery('/search', { q: 'test', page: 1, sort: 'desc', tags: ['active', 'user'] }) // => '/search?q=test&page=1&sort=desc&tags=active,user'
*
* // 处理无效值
* generateUrlWithQuery('/api/data', { id: 123, name: null, status: undefined }) // => '/api/data?id=123'
* generateUrlWithQuery('/api/data', { id: 123, name: null, status: undefined, empty: '', emptyArray: [] }) // => '/api/data?id=123'
*
* // 数组类型参数
* generateUrlWithQuery('/api/users', { ids: [1, 2, 3], tags: ['a', 'b'] }) // => '/api/users?ids=1,2,3&tags=a,b'
*
* // 嵌套数组(会被扁平化)
* generateUrlWithQuery('/api/data', { matrix: [[1, 2], [3, 4]] }) // => '/api/data?matrix=1,2,3,4'
*
* // 混合类型数组
* generateUrlWithQuery('/api/data', { mixed: [1, 'two', true, null, undefined] }) // => '/api/data?mixed=1,two,true,,'
*
* // 复杂类型数组(使用 toString() 转换)
* generateUrlWithQuery('/api/data', { objects: [{ a: 1 }, { b: 2 }] }) // => '/api/data?objects=[object Object],[object Object]'
* ```
*/
const generateUrlWithQuery = (pathname: string, queryParams: QueryParams = {}): string => {
Expand All @@ -44,8 +56,9 @@ const generateUrlWithQuery = (pathname: string, queryParams: QueryParams = {}):
const params = new URLSearchParams();

Object.entries(filteredParams).forEach(([key, value]) => {
if (['string', 'number', 'boolean'].includes(getTypeOfValue(value))) {
params.append(key, String(value));
if (['string', 'number', 'boolean', 'array'].includes(getTypeOfValue(value))) {
const stringValue = String(value);
stringValue && params.append(key, stringValue);
}
});

Expand Down
Loading