@peng_kai/kit 0.1.4 → 0.1.5
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/admin/components/filter/src/FilterReset.vue +2 -2
- package/admin/components/filter/src/useFilterParams.ts +9 -0
- package/admin/styles/classCover.scss +59 -2
- package/admin/types/assist.ts +2 -2
- package/antd/hooks/useAntdDrawer.ts +3 -3
- package/antd/hooks/useAntdTable.ts +13 -2
- package/package.json +2 -1
- package/pnpm-lock.yaml +13 -0
- package/utils/date.ts +44 -0
- package/utils/index.ts +14 -8
- package/utils/number.ts +49 -0
- package/vue/hooks/useComponentRef.ts +7 -1
- package/vue/hooks/useIsDark.ts +5 -0
- package/vue/hooks/useIsMounted.ts +3 -0
- package/vue/hooks/useTeleportTarget.ts +35 -2
- package/vue/index.ts +1 -1
|
@@ -29,7 +29,7 @@ const loading = computed(() => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
function filter() {
|
|
32
|
-
props.filterParams?.update?.();
|
|
32
|
+
props.filterParams?.update?.({ page: 1 });
|
|
33
33
|
emits('filter');
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -42,7 +42,7 @@ function reset() {
|
|
|
42
42
|
|
|
43
43
|
<template>
|
|
44
44
|
<div class="flex-none flex w-min ml-auto">
|
|
45
|
-
<AButton class="
|
|
45
|
+
<AButton class="filter-btn mr2" type="primary" htmlType="submit" :loading="loading" @click="filter()">
|
|
46
46
|
查询
|
|
47
47
|
</AButton>
|
|
48
48
|
<AButton :disabled="loading" @click="reset()">
|
|
@@ -4,6 +4,15 @@ import { reactive } from 'vue';
|
|
|
4
4
|
interface PageParams { page?: string | number, page_size?: string | number };
|
|
5
5
|
const defaultPageParams: PageParams = { page: 1, page_size: 10 };
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* 用于管理筛选参数
|
|
9
|
+
*
|
|
10
|
+
* @param _api - API 函数。
|
|
11
|
+
* @param buildParams - 构建参数的函数。
|
|
12
|
+
* @param pageParams - 默认的页面参数。
|
|
13
|
+
*
|
|
14
|
+
* @returns 包含筛选参数和更新函数的对象。
|
|
15
|
+
*/
|
|
7
16
|
export function useFilterParams<R extends Api.Request, AP extends Api.GetParam<R>, BP extends AP>(
|
|
8
17
|
_api: R,
|
|
9
18
|
buildParams: () => BP,
|
|
@@ -85,14 +85,71 @@
|
|
|
85
85
|
display: flex;
|
|
86
86
|
min-height: 56px;
|
|
87
87
|
align-items: center;
|
|
88
|
-
justify-content:
|
|
88
|
+
justify-content: flex-end;
|
|
89
89
|
padding: 0 var(--padding-size);
|
|
90
90
|
border-top: 1px solid var(--antd-colorBorderSecondary);
|
|
91
91
|
margin: 0;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
//
|
|
95
|
+
// 抽屉的基本款样式
|
|
96
|
+
.ant-drawer.antd-cover__basic-drawer {
|
|
97
|
+
--padding-size: 22px;
|
|
98
|
+
|
|
99
|
+
// --min-body-height: ;
|
|
100
|
+
// --max-body-height: ;
|
|
101
|
+
// --body-height: ;
|
|
102
|
+
|
|
103
|
+
.ant-drawer-content {
|
|
104
|
+
padding: 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.ant-drawer-close {
|
|
108
|
+
display: flex;
|
|
109
|
+
align-items: center;
|
|
110
|
+
justify-content: center;
|
|
111
|
+
width: 22px;
|
|
112
|
+
height: 22px;
|
|
113
|
+
padding: 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.ant-drawer-header {
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
min-height: 56px;
|
|
120
|
+
padding: 0 var(--padding-size);
|
|
121
|
+
margin: 0;
|
|
122
|
+
border-bottom: 1px solid var(--antd-colorBorderSecondary);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.ant-drawer-body {
|
|
126
|
+
height: var(--body-height, auto);
|
|
127
|
+
min-height: var(--min-body-height, auto);
|
|
128
|
+
max-height: var(--max-body-height, auto);
|
|
129
|
+
padding: var(--padding-size);
|
|
130
|
+
overflow-y: auto;
|
|
131
|
+
|
|
132
|
+
&:empty {
|
|
133
|
+
height: var(--min-body-height, 150px);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.ant-drawer-footer {
|
|
138
|
+
display: flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
justify-content: flex-end;
|
|
141
|
+
min-height: 56px;
|
|
142
|
+
padding: 0 var(--padding-size);
|
|
143
|
+
margin: 0;
|
|
144
|
+
border-top: 1px solid var(--antd-colorBorderSecondary);
|
|
145
|
+
|
|
146
|
+
& > *:not(:first-of-type) {
|
|
147
|
+
margin-left: 8px;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 查询器表单基本样式
|
|
96
153
|
.ant-form.ant-form__filter {
|
|
97
154
|
display: flex;
|
|
98
155
|
flex-wrap: wrap;
|
package/admin/types/assist.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare interface AppPermissionCodes {}
|
|
|
4
4
|
export declare interface AppRouteNames {}
|
|
5
5
|
|
|
6
6
|
/** 权限 code 类型辅助(permission code) */
|
|
7
|
-
export const
|
|
7
|
+
export const PC = createSelfKeyProxy<{ [K in keyof AppPermissionCodes]: K }>();
|
|
8
8
|
|
|
9
9
|
/** 路由 name 类型辅助(route name) */
|
|
10
|
-
export const
|
|
10
|
+
export const RN = createSelfKeyProxy<{ [K in keyof AppRouteNames]: K }>();
|
|
@@ -6,7 +6,7 @@ import type { ComponentProps } from 'vue-component-type-helpers';
|
|
|
6
6
|
import type { Writable } from 'type-fest';
|
|
7
7
|
import { useComponentRef } from '../../vue';
|
|
8
8
|
|
|
9
|
-
const defaultDrawerProps: DrawerProps = { open: false, destroyOnClose: true };
|
|
9
|
+
const defaultDrawerProps: DrawerProps = { open: false, destroyOnClose: true, rootClassName: 'antd-cover__basic-drawer' };
|
|
10
10
|
|
|
11
11
|
interface IComponentConfig<Comp extends Component> {
|
|
12
12
|
is: Comp
|
|
@@ -34,7 +34,7 @@ export function useAntdDrawer<Comp extends Component>(
|
|
|
34
34
|
_drawerProps.open = false;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
const
|
|
37
|
+
const DrawerFooter = defineComponent({
|
|
38
38
|
setup() {
|
|
39
39
|
const cancelBtnProps: ButtonProps = reactive({ onClick: close });
|
|
40
40
|
const confirmBtnProps: ButtonProps = reactive({
|
|
@@ -62,7 +62,7 @@ export function useAntdDrawer<Comp extends Component>(
|
|
|
62
62
|
},
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
_drawerProps.
|
|
65
|
+
_drawerProps.footer = _drawerProps.footer === undefined ? h(DrawerFooter) : _drawerProps.footer;
|
|
66
66
|
_drawerProps['onUpdate:open'] = (visiable) => {
|
|
67
67
|
_drawerProps.open = visiable;
|
|
68
68
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed } from 'vue';
|
|
1
|
+
import { computed, reactive } from 'vue';
|
|
2
2
|
import type { UseQueryReturnType } from '@tanstack/vue-query';
|
|
3
3
|
import type { Table, TableProps } from 'ant-design-vue';
|
|
4
4
|
import type { ColumnType } from 'ant-design-vue/es/table/interface';
|
|
@@ -11,6 +11,7 @@ export function useAntdTable<
|
|
|
11
11
|
type RecordType = GetRecordType<UQRR>;
|
|
12
12
|
type LocalTableProps = TableProps<RecordType>;
|
|
13
13
|
type LocalColumnsType = NonNullable<LocalTableProps['columns']>;
|
|
14
|
+
type LocalTableRowSelection = NonNullable<LocalTableProps['rowSelection']>;
|
|
14
15
|
|
|
15
16
|
const { data, isFetching, isLoading } = uqrt;
|
|
16
17
|
|
|
@@ -18,7 +19,15 @@ export function useAntdTable<
|
|
|
18
19
|
const page = queryParams.page_size !== pagination.pageSize ? 1 : pagination.current;
|
|
19
20
|
Object.assign(queryParams, { page, page_size: pagination.pageSize ?? 10 });
|
|
20
21
|
};
|
|
21
|
-
const defineColumns = (
|
|
22
|
+
const defineColumns = (columnsGetter: () => LocalColumnsType) => computed(columnsGetter);
|
|
23
|
+
const defineRowSelection = (rowSelectionGetter: () => LocalTableRowSelection) => {
|
|
24
|
+
const rowSelection = reactive(rowSelectionGetter());
|
|
25
|
+
|
|
26
|
+
rowSelection.selectedRowKeys ??= [];
|
|
27
|
+
rowSelection.onChange ??= keys => rowSelection.selectedRowKeys = keys;
|
|
28
|
+
|
|
29
|
+
return rowSelection;
|
|
30
|
+
};
|
|
22
31
|
|
|
23
32
|
const tableProps = computed<LocalTableProps>(() => {
|
|
24
33
|
const { list, pagination } = data.value ?? {};
|
|
@@ -60,6 +69,8 @@ export function useAntdTable<
|
|
|
60
69
|
bodyCellType,
|
|
61
70
|
/** 【类型辅助】用于定义出类型精确的 columns */
|
|
62
71
|
defineColumns,
|
|
72
|
+
/** 【类型辅助】用于定义出类型精确的 rowSelection */
|
|
73
|
+
defineRowSelection,
|
|
63
74
|
onPaginationChange,
|
|
64
75
|
};
|
|
65
76
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peng_kai/kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.5",
|
|
5
5
|
"description": "",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "ISC",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"@babel/types": "^7.23.5",
|
|
30
30
|
"@tanstack/vue-query": "^4.37.1",
|
|
31
31
|
"@vueuse/core": "^10.6.1",
|
|
32
|
+
"a-calc": "^1.3.8",
|
|
32
33
|
"ant-design-vue": "^4.0.7",
|
|
33
34
|
"axios": "^1.6.2",
|
|
34
35
|
"bignumber.js": "^9.1.2",
|
package/pnpm-lock.yaml
CHANGED
|
@@ -23,6 +23,9 @@ dependencies:
|
|
|
23
23
|
'@vueuse/core':
|
|
24
24
|
specifier: ^10.6.1
|
|
25
25
|
version: 10.6.1(vue@3.3.8)
|
|
26
|
+
a-calc:
|
|
27
|
+
specifier: ^1.3.8
|
|
28
|
+
version: 1.3.8
|
|
26
29
|
ant-design-vue:
|
|
27
30
|
specifier: ^4.0.7
|
|
28
31
|
version: 4.0.7(vue@3.3.8)
|
|
@@ -1549,6 +1552,12 @@ packages:
|
|
|
1549
1552
|
through: 2.3.8
|
|
1550
1553
|
dev: true
|
|
1551
1554
|
|
|
1555
|
+
/a-calc@1.3.8:
|
|
1556
|
+
resolution: {integrity: sha512-/YQ+RActls7GQYXjCYP135DEwMC7tdUFfEiR65pnjsZ4N1Uvtz3MKlpw2RHI0hoDraJ2Ih9zjcAAERxnKL4qng==}
|
|
1557
|
+
dependencies:
|
|
1558
|
+
typescript-treasure: 0.0.9
|
|
1559
|
+
dev: false
|
|
1560
|
+
|
|
1552
1561
|
/acorn-jsx@5.3.2(acorn@8.11.2):
|
|
1553
1562
|
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
|
1554
1563
|
peerDependencies:
|
|
@@ -4963,6 +4972,10 @@ packages:
|
|
|
4963
4972
|
engines: {node: '>=16'}
|
|
4964
4973
|
dev: true
|
|
4965
4974
|
|
|
4975
|
+
/typescript-treasure@0.0.9:
|
|
4976
|
+
resolution: {integrity: sha512-QPmpqJvQqZ7rt2iVNzPrtQNSFs1zVuTuP+jzE3np7qytEUcfSKtAW8RTDslldeAwaJjVJa0UM3ps1uVddpEMqQ==}
|
|
4977
|
+
dev: false
|
|
4978
|
+
|
|
4966
4979
|
/typescript@5.2.2:
|
|
4967
4980
|
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
|
|
4968
4981
|
engines: {node: '>=14.17'}
|
package/utils/date.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import relativeTime from 'dayjs/plugin/relativeTime';
|
|
3
|
+
import 'dayjs/locale/zh';
|
|
4
|
+
import 'dayjs/locale/en';
|
|
5
|
+
|
|
6
|
+
export { default as dayjs } from 'dayjs';
|
|
7
|
+
|
|
8
|
+
dayjs.locale('zh');
|
|
9
|
+
dayjs.extend(relativeTime);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 返回给定日期的一天的开始时间。
|
|
13
|
+
* @param day - 要获取一天的开始时间的日期。
|
|
14
|
+
* @returns 一天的开始时间作为Unix时间戳,如果输入无效则返回 undefined。
|
|
15
|
+
*/
|
|
16
|
+
export function startOfDay(day: dayjs.ConfigType) {
|
|
17
|
+
const _day = dayjs(day);
|
|
18
|
+
return _day.isValid() ? _day.startOf('day').valueOf() : undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 计算给定日期的当天结束时间。
|
|
23
|
+
* @param day - 要计算当天结束时间的日期。
|
|
24
|
+
* @returns 表示当天结束时间的时间戳值,如果输入无效则返回 undefined。
|
|
25
|
+
*/
|
|
26
|
+
export function endOfDay(day: dayjs.ConfigType) {
|
|
27
|
+
const _day = dayjs(day);
|
|
28
|
+
return _day.isValid() ? _day.endOf('day').valueOf() : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 将秒数转换为时长格式化时间
|
|
33
|
+
* @param seconds 秒数
|
|
34
|
+
* @returns 时长格式化时间
|
|
35
|
+
*/
|
|
36
|
+
export function secondsToDuration(seconds: number) {
|
|
37
|
+
const units = [['天', 24 * 60 * 60], ['时', 60 * 60], ['分', 60], ['秒', 1]] as const;
|
|
38
|
+
|
|
39
|
+
return units.reduce((duration, [label, durationValue]) => {
|
|
40
|
+
const value = Math.floor(seconds / durationValue);
|
|
41
|
+
seconds %= durationValue;
|
|
42
|
+
return value > 0 ? duration += `${value + label}` : duration;
|
|
43
|
+
}, '');
|
|
44
|
+
}
|
package/utils/index.ts
CHANGED
|
@@ -18,9 +18,7 @@ export const ENV = {
|
|
|
18
18
|
* @param dur 时长
|
|
19
19
|
*/
|
|
20
20
|
export function sleep(dur: number) {
|
|
21
|
-
return new Promise(
|
|
22
|
-
setTimeout(resolve, dur);
|
|
23
|
-
});
|
|
21
|
+
return new Promise(resolve => setTimeout(resolve, dur));
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
/**
|
|
@@ -34,11 +32,6 @@ export function isSSR() {
|
|
|
34
32
|
return typeof window === 'undefined';
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
export function fixed() {
|
|
38
|
-
|
|
39
|
-
// return (12).toFixed
|
|
40
|
-
}
|
|
41
|
-
|
|
42
35
|
/**
|
|
43
36
|
* 字符串脱敏,省略中间字符串
|
|
44
37
|
* @param str 字符串
|
|
@@ -59,6 +52,15 @@ export function desensitize(str: string | undefined | null, startChars = 4, endC
|
|
|
59
52
|
return truncatedStr;
|
|
60
53
|
}
|
|
61
54
|
|
|
55
|
+
/**
|
|
56
|
+
* 生成指定长度的随机字符串
|
|
57
|
+
* @param length 要生成的随机字符串的长度
|
|
58
|
+
*/
|
|
59
|
+
export function randomString(length: number) {
|
|
60
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
61
|
+
return Array.from({ length }, () => characters[Math.floor(Math.random() * characters.length)]).join('');
|
|
62
|
+
}
|
|
63
|
+
|
|
62
64
|
/**
|
|
63
65
|
* 对应链的浏览器地址
|
|
64
66
|
* @param chain 链名
|
|
@@ -121,6 +123,10 @@ export function removeUrlSearchParam(key: string) {
|
|
|
121
123
|
window.history.replaceState({}, '', url);
|
|
122
124
|
}
|
|
123
125
|
|
|
126
|
+
/**
|
|
127
|
+
* 创建一个带有自身键的代理对象
|
|
128
|
+
* @returns 带有自身键的代理对象
|
|
129
|
+
*/
|
|
124
130
|
export function createSelfKeyProxy<T extends object>() {
|
|
125
131
|
return new Proxy({} as T, {
|
|
126
132
|
get(_, p) {
|
package/utils/number.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { calc } from 'a-calc';
|
|
2
|
+
|
|
3
|
+
export { default as bn } from 'bignumber.js';
|
|
4
|
+
export { calc } from 'a-calc';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 常用的数值格式化规则
|
|
8
|
+
*
|
|
9
|
+
* 命名规则:
|
|
10
|
+
* - d-decimal: 保留的小数位
|
|
11
|
+
* - p-pad: 当小数位不够填充指定的小数位时,是否填充0
|
|
12
|
+
* - r-round: 舍入规则。可选规则: R5四舍五入、R6四舍六入、R1向上取整、R0向下取整
|
|
13
|
+
* - t-thousand: 是否使用千位符
|
|
14
|
+
* - n-number: 返回数字类型
|
|
15
|
+
*/
|
|
16
|
+
export const numFmt = {
|
|
17
|
+
/** 保留18位小数,千位符 */
|
|
18
|
+
t: (num: any, err: any = null) => calc('a | <=18 ,', { a: String(num), _error: err }) as string | null,
|
|
19
|
+
/** 舍去小数,四舍五入,千位符 */
|
|
20
|
+
d0r5t: (num: any, err: any = null) => calc('a | =0 ~5 ,', { a: String(num), _error: err }) as string | null,
|
|
21
|
+
/** 舍去小数,向下取整,千位符 */
|
|
22
|
+
d0r0t: (num: any, err: any = null) => calc('a | =0 ~- ,', { a: String(num), _error: err }) as string | null,
|
|
23
|
+
/** 保留2位小数,四舍五入,千位符 */
|
|
24
|
+
d2r5t: (num: any, err: any = null) => calc('a | <=2 ~5 ,', { a: String(num), _error: err }) as string | null,
|
|
25
|
+
/** 保留2位小数,向下取整,千位符 */
|
|
26
|
+
d2r0t: (num: any, err: any = null) => calc('a | <=2 ~- ,', { a: String(num), _error: err }) as string | null,
|
|
27
|
+
/** 保留6位小数,四舍五入,千位符 */
|
|
28
|
+
d6r5t: (num: any, err: any = null) => calc('a | <=6 ~5 ,', { a: String(num), _error: err }) as string | null,
|
|
29
|
+
/** 保留6位小数,向下取整,千位符 */
|
|
30
|
+
d6r0t: (num: any, err: any = null) => calc('a | <=6 ~- ,', { a: String(num), _error: err }) as string | null,
|
|
31
|
+
/** 保留8位小数,四舍五入,千位符 */
|
|
32
|
+
d8r5t: (num: any, err: any = null) => calc('a | <=8 ~5 ,', { a: String(num), _error: err }) as string | null,
|
|
33
|
+
/** 保留8位小数,向下取整,千位符 */
|
|
34
|
+
d8r0t: (num: any, err: any = null) => calc('a | <=8 ~- ,', { a: String(num), _error: err }) as string | null,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 将给定的金额转换为格式化的字符串表示
|
|
39
|
+
* @param amount - 要转换的金额。
|
|
40
|
+
* @param decimal - 格式化字符串中包含的小数位数。默认为 0。
|
|
41
|
+
* @returns 金额的格式化字符串表示。
|
|
42
|
+
*/
|
|
43
|
+
export function toAmount(amount: any, decimal: string | number = 0) {
|
|
44
|
+
const res = calc('a / 10 ** d', { a: String(amount), d: Number(decimal), _error: null });
|
|
45
|
+
const fmt = (fmt: keyof typeof numFmt) => (numFmt[fmt] ?? numFmt.t)(res);
|
|
46
|
+
fmt.toString = () => numFmt.t(res);
|
|
47
|
+
|
|
48
|
+
return fmt;
|
|
49
|
+
}
|
|
@@ -2,7 +2,13 @@ import { ref } from 'vue';
|
|
|
2
2
|
import type { Component } from 'vue';
|
|
3
3
|
import type { ComponentExposed } from 'vue-component-type-helpers';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* 组件引用
|
|
7
|
+
* @param component 组件实例
|
|
8
|
+
* @returns 组件引用的代理对象
|
|
9
|
+
*/
|
|
10
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
11
|
+
export function useComponentRef<C extends Component>(component: C) {
|
|
6
12
|
const _cpt = ref<any>();
|
|
7
13
|
const refFn = (cpt: any) => {
|
|
8
14
|
_cpt.value = cpt;
|
package/vue/hooks/useIsDark.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import { onMounted, ref } from 'vue';
|
|
1
|
+
import { h, onMounted, ref } from 'vue';
|
|
2
|
+
import type { VNode } from 'vue';
|
|
3
|
+
import type { ArrayValues } from 'type-fest';
|
|
4
|
+
import { randomString } from '../../utils';
|
|
2
5
|
|
|
6
|
+
/**
|
|
7
|
+
* 使用选择器获取传送目标
|
|
8
|
+
* @param selectors 选择器字符串
|
|
9
|
+
* @returns 传送目标的引用
|
|
10
|
+
*/
|
|
3
11
|
export function useTeleportTarget(selectors: string) {
|
|
4
12
|
const target = ref<HTMLElement | null>();
|
|
5
13
|
|
|
@@ -10,4 +18,29 @@ export function useTeleportTarget(selectors: string) {
|
|
|
10
18
|
});
|
|
11
19
|
|
|
12
20
|
return target;
|
|
13
|
-
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 基于提供的顶级类和目标创建传送门选择器
|
|
25
|
+
* @param topClass 传送门选择器的顶级类
|
|
26
|
+
* @param targets 传送门的目标
|
|
27
|
+
*/
|
|
28
|
+
export function createTeleportSelectors<TG extends string[]>(topClass: string, targets: TG) {
|
|
29
|
+
type TGK = ArrayValues<TG>;
|
|
30
|
+
type TRet = { topClass: string }
|
|
31
|
+
& { [K in `${TGK}Selector`]: string }
|
|
32
|
+
& { [K in `${TGK}Class`]: string }
|
|
33
|
+
& { [K in `${TGK}Target`]: (className?: string, tag?: string) => VNode };
|
|
34
|
+
|
|
35
|
+
const ret: Record<string, any> = {};
|
|
36
|
+
ret.topClass = `${topClass}-${randomString(6)}`;
|
|
37
|
+
|
|
38
|
+
targets.forEach((target) => {
|
|
39
|
+
const _target = ret[`${target}Class`] = `${target}_${randomString(6)}`;
|
|
40
|
+
|
|
41
|
+
ret[`${target}Selector`] = `.${ret.topClass} .${_target}`;
|
|
42
|
+
ret[`${target}Target`] = (className: string = '', tag: string = 'div') => h(tag, { className: `${_target} ${className}` });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return ret as TRet;
|
|
46
|
+
}
|
package/vue/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { useComponentRef } from './hooks/useComponentRef';
|
|
2
|
-
export { useTeleportTarget } from './hooks/useTeleportTarget';
|
|
2
|
+
export { useTeleportTarget, createTeleportSelectors } from './hooks/useTeleportTarget';
|
|
3
3
|
export { useIsMounted } from './hooks/useIsMounted';
|
|
4
4
|
export { useIsDark } from './hooks/useIsDark';
|
|
5
5
|
export { InfiniteQuery } from './components/infinite-query';
|