@peng_kai/kit 0.1.17 → 0.2.0-beta.1

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.
Files changed (84) hide show
  1. package/admin/adminPlugin.ts +47 -0
  2. package/admin/components/filter/src/FilterDrawer.vue +153 -153
  3. package/admin/components/filter/src/FilterParam.vue +76 -78
  4. package/admin/components/filter/src/FilterReset.vue +2 -2
  5. package/admin/components/filter/src/useFilterParams.ts +75 -25
  6. package/admin/components/filter/src/useFilterQuery.ts +32 -16
  7. package/admin/components/rich-text/index.ts +2 -0
  8. package/admin/components/rich-text/src/RichText.vue +342 -0
  9. package/admin/components/rich-text/src/imageUploader.ts +34 -0
  10. package/admin/components/rich-text/src/type.d.ts +7 -0
  11. package/admin/components/scroll-nav/index.ts +1 -1
  12. package/admin/components/scroll-nav/src/ScrollNav.vue +59 -59
  13. package/admin/components/text/index.ts +13 -13
  14. package/admin/components/text/src/Amount.vue +121 -121
  15. package/admin/components/text/src/Datetime.vue +47 -48
  16. package/admin/components/text/src/Duration.vue +26 -26
  17. package/admin/components/text/src/Hash.vue +51 -51
  18. package/admin/components/text/src/createTagGetter.ts +13 -13
  19. package/admin/components/upload/index.ts +2 -0
  20. package/admin/components/upload/src/PictureCardUpload.vue +143 -0
  21. package/admin/components/upload/src/customRequests.ts +31 -0
  22. package/admin/defines/index.ts +1 -1
  23. package/admin/defines/route/helpers.ts +0 -1
  24. package/admin/defines/startup/defineStartup.ts +8 -1
  25. package/admin/defines/startup/index.ts +1 -1
  26. package/admin/defines/startup/{getStartups.ts → runStartup.ts} +16 -7
  27. package/admin/layout/large/Breadcrumb.vue +68 -69
  28. package/admin/layout/large/Content.vue +23 -24
  29. package/admin/layout/large/Menu.vue +68 -69
  30. package/admin/layout/large/PageTab.vue +70 -71
  31. package/admin/permission/index.ts +2 -4
  32. package/admin/permission/routerGuard.ts +41 -43
  33. package/admin/permission/vuePlugin.ts +46 -30
  34. package/admin/route-guards/collapseMenu.ts +3 -3
  35. package/admin/route-guards/index.ts +3 -3
  36. package/admin/route-guards/pageProgress.ts +27 -27
  37. package/admin/route-guards/pageTitle.ts +18 -19
  38. package/admin/{hooks/useMenu.ts → stores/createUseMenuStore.ts} +133 -128
  39. package/admin/{hooks/usePage.ts → stores/createUsePageStore.ts} +145 -141
  40. package/admin/stores/createUsePageTabStore.ts +43 -0
  41. package/admin/{permission/usePermission.ts → stores/createUsePermissionStore.ts} +57 -52
  42. package/admin/stores/index.ts +8 -0
  43. package/admin/styles/classCover.scss +8 -0
  44. package/admin/styles/globalCover.scss +54 -54
  45. package/antd/components/InputNumberRange.vue +59 -59
  46. package/antd/directives/formLabelAlign.ts +36 -36
  47. package/antd/hooks/useAntdDrawer.ts +73 -73
  48. package/antd/hooks/useAntdForm.helpers.ts +92 -8
  49. package/antd/hooks/useAntdForm.ts +55 -63
  50. package/antd/hooks/useAntdTable.ts +127 -115
  51. package/antd/index.ts +1 -1
  52. package/libs/a-calc.ts +1 -0
  53. package/libs/axios.ts +2 -0
  54. package/libs/bignumber.ts +2 -0
  55. package/libs/dayjs.ts +5 -0
  56. package/libs/echarts.ts +5 -0
  57. package/libs/localstorage-slim.ts +2 -0
  58. package/libs/lodash-es.ts +1 -0
  59. package/libs/pinia.ts +1 -0
  60. package/libs/vue-query.ts +1 -0
  61. package/libs/vueuse.ts +3 -0
  62. package/package.json +91 -58
  63. package/request/helpers.ts +68 -49
  64. package/request/interceptors/toLogin.ts +26 -26
  65. package/request/queryClient.ts +34 -21
  66. package/request/type.d.ts +92 -92
  67. package/stylelint.config.cjs +7 -7
  68. package/tsconfig.json +50 -50
  69. package/utils/upload/AwsS3.ts +68 -0
  70. package/utils/upload/fileHandlers.ts +27 -0
  71. package/utils/upload/index.ts +2 -0
  72. package/vite/index.d.ts +1 -0
  73. package/vite/index.mjs +27 -0
  74. package/vue/components/echarts/index.ts +1 -0
  75. package/vue/components/echarts/src/ECharts.vue +48 -0
  76. package/vue/components/index.ts +1 -0
  77. package/vue/components/infinite-query/index.ts +1 -1
  78. package/vue/components/infinite-query/src/InfiniteQuery.vue +199 -205
  79. package/vue/components/infinite-query/src/useCreateTrigger.ts +39 -39
  80. package/vue/components/test/KitTest.vue +9 -0
  81. package/vue/components/test/testStore.ts +11 -0
  82. package/admin/hooks/index.ts +0 -5
  83. package/admin/hooks/usePageTab.ts +0 -35
  84. package/kitDependencies.ts +0 -43
@@ -1,59 +1,59 @@
1
- <script lang="ts">
2
- import { computed } from 'vue';
3
- import { InputNumber as AInputNumber, Form } from 'ant-design-vue';
4
- </script>
5
-
6
- <script setup lang="ts">
7
- const props = withDefaults(
8
- defineProps<{
9
- value: [number | undefined, number | undefined]
10
- placeholder?: [string, string]
11
- min?: number
12
- max?: number
13
- }>(),
14
- {
15
- min: Number.NEGATIVE_INFINITY,
16
- max: Number.POSITIVE_INFINITY,
17
- },
18
- );
19
- const emits = defineEmits<{
20
- (e: 'update:value', value: typeof props.value): void
21
- }>();
22
-
23
- const formItemContext = Form.useInjectFormItemContext();
24
- const minValue = computed({
25
- get() {
26
- return props.value[0];
27
- },
28
- set(value) {
29
- updateValue(value, maxValue.value);
30
- },
31
- });
32
- const maxValue = computed({
33
- get() {
34
- return props.value[1];
35
- },
36
- set(value) {
37
- updateValue(minValue.value, value);
38
- },
39
- });
40
-
41
- function updateValue(...args: typeof props.value) {
42
- emits('update:value', args.sort());
43
- formItemContext.onFieldChange();
44
- }
45
- </script>
46
-
47
- <template>
48
- <div class="flex items-center">
49
- <AInputNumber
50
- v-model:value="minValue" class="w-full" :min="props.min"
51
- :max="props.max" :placeholder="props.placeholder?.[0]"
52
- />
53
- <span>&nbsp;-&nbsp;</span>
54
- <AInputNumber
55
- v-model:value="maxValue" class="w-full" :min="props.min"
56
- :max="props.max" :placeholder="props.placeholder?.[1]"
57
- />
58
- </div>
59
- </template>
1
+ <script lang="ts">
2
+ import { computed } from 'vue';
3
+ import { InputNumber as AInputNumber, Form } from 'ant-design-vue';
4
+ </script>
5
+
6
+ <script setup lang="ts">
7
+ const props = withDefaults(
8
+ defineProps<{
9
+ value: [number | undefined, number | undefined]
10
+ placeholder?: [string, string]
11
+ min?: number
12
+ max?: number
13
+ }>(),
14
+ {
15
+ min: Number.NEGATIVE_INFINITY,
16
+ max: Number.POSITIVE_INFINITY,
17
+ },
18
+ );
19
+ const emits = defineEmits<{
20
+ (e: 'update:value', value: typeof props.value): void
21
+ }>();
22
+
23
+ const formItemContext = Form.useInjectFormItemContext();
24
+ const minValue = computed({
25
+ get() {
26
+ return props.value[0];
27
+ },
28
+ set(value) {
29
+ updateValue(value, maxValue.value);
30
+ },
31
+ });
32
+ const maxValue = computed({
33
+ get() {
34
+ return props.value[1];
35
+ },
36
+ set(value) {
37
+ updateValue(minValue.value, value);
38
+ },
39
+ });
40
+
41
+ function updateValue(...args: typeof props.value) {
42
+ emits('update:value', args.sort());
43
+ formItemContext.onFieldChange();
44
+ }
45
+ </script>
46
+
47
+ <template>
48
+ <div class="flex items-center">
49
+ <AInputNumber
50
+ v-model:value="minValue" class="w-full" :min="props.min"
51
+ :max="props.max" :placeholder="props.placeholder?.[0]"
52
+ />
53
+ <span>&nbsp;-&nbsp;</span>
54
+ <AInputNumber
55
+ v-model:value="maxValue" class="w-full" :min="props.min"
56
+ :max="props.max" :placeholder="props.placeholder?.[1]"
57
+ />
58
+ </div>
59
+ </template>
@@ -1,36 +1,36 @@
1
- import type { App } from 'vue';
2
-
3
- export function formLabelAlign(app: App) {
4
- const directiveName = 'antd-form-label-align';
5
- const resizeObserverKey = `${directiveName}@resizeObserver`;
6
-
7
- function init(el: HTMLElement) {
8
- const labels = el.querySelectorAll('.ant-form-item .ant-form-item-label');
9
- const resizeObserver = new ResizeObserver((entries) => {
10
- const widths = entries.map(e => e.borderBoxSize?.[0]?.inlineSize ?? 0);
11
- const maxWidth = Math.max(...widths);
12
-
13
- if (maxWidth <= 0)
14
- return;
15
-
16
- el.style.setProperty('--max-label-width', `${maxWidth}px`);
17
- entries.forEach((e) => {
18
- const target = e.target as HTMLElement;
19
- target.style.setProperty('width', 'var(--max-label-width)');
20
- });
21
- });
22
-
23
- Array.from(labels).forEach(label => resizeObserver.observe(label));
24
-
25
- return resizeObserver;
26
- }
27
-
28
- app.directive(directiveName, {
29
- mounted(el: HTMLElement) {
30
- (el as any)[resizeObserverKey] = init(el);
31
- },
32
- updated(el) {
33
- (el as any)[resizeObserverKey] = init(el);
34
- },
35
- });
36
- }
1
+ import type { App } from 'vue';
2
+
3
+ export function formLabelAlign(app: App) {
4
+ const directiveName = 'antd-form-label-align';
5
+ const resizeObserverKey = `${directiveName}@resizeObserver`;
6
+
7
+ function init(el: HTMLElement) {
8
+ const labels = el.querySelectorAll('.ant-form-item .ant-form-item-label');
9
+ const resizeObserver = new ResizeObserver((entries) => {
10
+ const widths = entries.map(e => e.borderBoxSize?.[0]?.inlineSize ?? 0);
11
+ const maxWidth = Math.max(...widths);
12
+
13
+ if (maxWidth <= 0)
14
+ return;
15
+
16
+ el.style.setProperty('--max-label-width', `${maxWidth}px`);
17
+ entries.forEach((e) => {
18
+ const target = e.target as HTMLElement;
19
+ target.style.setProperty('width', 'var(--max-label-width)');
20
+ });
21
+ });
22
+
23
+ Array.from(labels).forEach(label => resizeObserver.observe(label));
24
+
25
+ return resizeObserver;
26
+ }
27
+
28
+ app.directive(directiveName, {
29
+ mounted(el: HTMLElement) {
30
+ (el as any)[resizeObserverKey] = init(el);
31
+ },
32
+ updated(el) {
33
+ (el as any)[resizeObserverKey] = init(el);
34
+ },
35
+ });
36
+ }
@@ -1,73 +1,73 @@
1
- import { Button as AButton, Drawer as ADrawer, Space as ASpace } from 'ant-design-vue';
2
- import { defineComponent, h, isProxy, reactive, toRef, toRefs } from 'vue';
3
- import type { Component } from 'vue';
4
- import type { ButtonProps, DrawerProps } from 'ant-design-vue';
5
- import type { ComponentProps } from 'vue-component-type-helpers';
6
- import type { Writable } from 'type-fest';
7
- import { useComponentRef } from '../../vue';
8
-
9
- const defaultDrawerProps: DrawerProps = { open: false, destroyOnClose: true, rootClassName: 'antd-cover__basic-drawer' };
10
-
11
- interface IComponentConfig<Comp extends Component> {
12
- is: Comp
13
- props?: Writable<ComponentProps<Comp>>
14
- }
15
-
16
- export function useAntdDrawer<Comp extends Component>(
17
- comp: IComponentConfig<Comp> | Comp,
18
- drawerProps = defaultDrawerProps,
19
- ) {
20
- const _comp = ({ props: {}, ...((comp as any)?.is ? comp : { is: comp }) }) as Required<IComponentConfig<Comp>>;
21
- const compProps = reactive(_comp.props);
22
- const compRef = useComponentRef(_comp.is);
23
- const _drawerProps: DrawerProps = reactive({
24
- ...defaultDrawerProps,
25
- ...isProxy(drawerProps) ? toRefs(drawerProps) : drawerProps,
26
- });
27
-
28
- const open = (newBodyProps?: Partial<typeof compProps>, newAntdModalProps?: Omit<Partial<DrawerProps>, 'open'>) => {
29
- Object.assign(_drawerProps, newAntdModalProps);
30
- Object.assign(compProps, newBodyProps);
31
- _drawerProps.open = true;
32
- };
33
- const close = () => {
34
- _drawerProps.open = false;
35
- };
36
-
37
- const DrawerFooter = defineComponent({
38
- setup() {
39
- const cancelBtnProps: ButtonProps = reactive({ onClick: close });
40
- const confirmBtnProps: ButtonProps = reactive({
41
- type: 'primary',
42
- loading: toRef(() => (compRef as any)?.loading),
43
- onClick: () => (compRef as any)?.confirm?.(),
44
- });
45
-
46
- return { cancelBtnProps, confirmBtnProps };
47
- },
48
- render() {
49
- const { cancelBtnProps, confirmBtnProps } = this;
50
-
51
- return h(ASpace, {}, () => [
52
- h(AButton, cancelBtnProps, () => '取消'),
53
- h(AButton, confirmBtnProps, () => '确定'),
54
- ]);
55
- },
56
- });
57
- const PresetComponent = defineComponent({
58
- render() {
59
- return h(ADrawer, _drawerProps, {
60
- default: () => h(_comp.is, compProps as any),
61
- });
62
- },
63
- });
64
-
65
- _drawerProps.footer = _drawerProps.footer === undefined ? h(DrawerFooter) : _drawerProps.footer;
66
- _drawerProps['onUpdate:open'] = (visiable) => {
67
- _drawerProps.open = visiable;
68
- };
69
- (compProps as any).ref = compRef;
70
- (compProps as any).onClose = close;
71
-
72
- return { PresetComponent, drawerProps: _drawerProps, open, close };
73
- }
1
+ import { Button as AButton, Drawer as ADrawer, Space as ASpace } from 'ant-design-vue';
2
+ import { defineComponent, h, isProxy, reactive, toRef, toRefs } from 'vue';
3
+ import type { Component } from 'vue';
4
+ import type { ButtonProps, DrawerProps } from 'ant-design-vue';
5
+ import type { ComponentProps } from 'vue-component-type-helpers';
6
+ import type { Writable } from 'type-fest';
7
+ import { useComponentRef } from '../../vue';
8
+
9
+ const defaultDrawerProps: DrawerProps = { open: false, destroyOnClose: true, rootClassName: 'antd-cover__basic-drawer' };
10
+
11
+ interface IComponentConfig<Comp extends Component> {
12
+ is: Comp
13
+ props?: Writable<ComponentProps<Comp>>
14
+ }
15
+
16
+ export function useAntdDrawer<Comp extends Component>(
17
+ comp: IComponentConfig<Comp> | Comp,
18
+ drawerProps = defaultDrawerProps,
19
+ ) {
20
+ const _comp = ({ props: {}, ...((comp as any)?.is ? comp : { is: comp }) }) as Required<IComponentConfig<Comp>>;
21
+ const compProps = reactive(_comp.props);
22
+ const compRef = useComponentRef(_comp.is);
23
+ const _drawerProps: DrawerProps = reactive({
24
+ ...defaultDrawerProps,
25
+ ...isProxy(drawerProps) ? toRefs(drawerProps) : drawerProps,
26
+ });
27
+
28
+ const open = (newBodyProps?: Partial<typeof compProps>, newAntdModalProps?: Omit<Partial<DrawerProps>, 'open'>) => {
29
+ Object.assign(_drawerProps, newAntdModalProps);
30
+ Object.assign(compProps, newBodyProps);
31
+ _drawerProps.open = true;
32
+ };
33
+ const close = () => {
34
+ _drawerProps.open = false;
35
+ };
36
+
37
+ const DrawerFooter = defineComponent({
38
+ setup() {
39
+ const cancelBtnProps: ButtonProps = reactive({ onClick: close });
40
+ const confirmBtnProps: ButtonProps = reactive({
41
+ type: 'primary',
42
+ loading: toRef(() => (compRef as any)?.loading),
43
+ onClick: () => (compRef as any)?.confirm?.(),
44
+ });
45
+
46
+ return { cancelBtnProps, confirmBtnProps };
47
+ },
48
+ render() {
49
+ const { cancelBtnProps, confirmBtnProps } = this;
50
+
51
+ return h(ASpace, {}, () => [
52
+ h(AButton, cancelBtnProps, () => '取消'),
53
+ h(AButton, confirmBtnProps, () => '确定'),
54
+ ]);
55
+ },
56
+ });
57
+ const PresetComponent = defineComponent({
58
+ render() {
59
+ return h(ADrawer, _drawerProps, {
60
+ default: () => h(_comp.is, compProps as any),
61
+ });
62
+ },
63
+ });
64
+
65
+ _drawerProps.footer = _drawerProps.footer === undefined ? h(DrawerFooter) : _drawerProps.footer;
66
+ _drawerProps['onUpdate:open'] = (visiable) => {
67
+ _drawerProps.open = visiable;
68
+ };
69
+ (compProps as any).ref = compRef;
70
+ (compProps as any).onClose = close;
71
+
72
+ return { PresetComponent, drawerProps: _drawerProps, open, close };
73
+ }
@@ -1,15 +1,38 @@
1
- export { buildGroupFieldName, parseGroupFieldName, isSameGroup, isSameField, getGroupIndex, GROUP_SEP };
1
+ import type { FormItemProps } from 'ant-design-vue/es/form';
2
+
3
+ export {
4
+ buildGroupField,
5
+ parseGroupField,
6
+ groupingForm,
7
+ formatGroup,
8
+ isSameGroup,
9
+ isSameField,
10
+ getGroupIndex,
11
+ GROUP_SEP,
12
+ };
2
13
 
3
14
  const GROUP_SEP = '__';
4
15
  const groupFieldNameRE = new RegExp(
5
- `(?<groupName>[a-zA-Z0-9]+)${GROUP_SEP}(?<index>\\d+)${GROUP_SEP}(?<fieldName>[a-zA-Z0-9]+)`,
16
+ `(?<groupName>.+?)${GROUP_SEP}(?<index>\\d+)${GROUP_SEP}(?<fieldName>.+)`,
6
17
  );
7
18
 
8
- function buildGroupFieldName(name: string, index: number, field: string) {
19
+ /**
20
+ * 构建组字段名
21
+ * @param name - 组名
22
+ * @param index - 组索引
23
+ * @param field - 字段名
24
+ * @returns 返回组字段名
25
+ */
26
+ function buildGroupField(name: string, index: number, field: string) {
9
27
  return name + GROUP_SEP + index + GROUP_SEP + field;
10
28
  }
11
29
 
12
- function parseGroupFieldName(name: string) {
30
+ /**
31
+ * 解析组字段名
32
+ * @param name - 组字段名
33
+ * @returns 返回一个数组,包含组名、组索引和字段名
34
+ */
35
+ function parseGroupField(name: string) {
13
36
  const res = name.match(groupFieldNameRE);
14
37
  const ret = {
15
38
  fieldName: '',
@@ -26,23 +49,84 @@ function parseGroupFieldName(name: string) {
26
49
  return ret;
27
50
  }
28
51
 
52
+ /**
53
+ * 根据指定的组名对表单项进行分组
54
+ * @param groupName - 要分组的组名
55
+ * @param items - 包含表单项的对象
56
+ * @returns 返回一个对象,其键为组索引,值为该组内的表单项
57
+ */
58
+ function groupingForm(groupName: string, items: Record<string, FormItemProps>) {
59
+ const _groups: Record<number, typeof items> = {};
60
+
61
+ for (const [key, item] of Object.entries(items)) {
62
+ const { groupName: name, index } = parseGroupField(key);
63
+
64
+ if (name === groupName) {
65
+ _groups[index] ??= {};
66
+ _groups[index][key] = item;
67
+ }
68
+ }
69
+
70
+ return _groups;
71
+ }
72
+
73
+ /**
74
+ * 格式化表单项组
75
+ * @param name - 组名
76
+ * @param items - 包含表单数据
77
+ * @returns 返回一个数组,其元素为分组后的表单项对象,如果没有找到匹配的组名,则返回 undefined
78
+ */
79
+ function formatGroup(name: string, items: Record<string, any>) {
80
+ const group: any[] = [];
81
+
82
+ for (const k in items) {
83
+ const params = parseGroupField(k);
84
+
85
+ if (params.groupName !== name || params.index === -1)
86
+ continue;
87
+
88
+ group[params.index] ??= {};
89
+ group[params.index][params.fieldName] = items[k];
90
+ }
91
+
92
+ return group?.length ? group.filter(g => !!g) : undefined;
93
+ }
94
+
95
+ /**
96
+ * 获取指定组名的最大索引值
97
+ * @param name - 组名
98
+ * @param schemas - 包含组字段的对象
99
+ * @returns 返回指定组名的最大索引值,如果没有找到匹配的组名,则返回 -1
100
+ */
29
101
  function getGroupIndex(name: string, schemas: Record<string, any>) {
30
102
  const indexs = Object.keys(schemas).map((key) => {
31
- const { groupName, index } = parseGroupFieldName(key);
103
+ const { groupName, index } = parseGroupField(key);
32
104
  return groupName === name ? index : -1;
33
105
  });
34
106
 
35
- return Math.max.apply(undefined, indexs);
107
+ return Math.max(...indexs);
36
108
  }
37
109
 
110
+ /**
111
+ * 判断指定的名称是否属于同一组
112
+ * @param name - 要检查的名称
113
+ * @param groupName - 组名
114
+ * @returns 如果指定的名称属于同一组,则返回 true,否则返回 false
115
+ */
38
116
  function isSameGroup(name: string, groupName: string) {
39
- const { groupName: gn } = parseGroupFieldName(name);
117
+ const { groupName: gn } = parseGroupField(name);
40
118
 
41
119
  return gn === groupName;
42
120
  }
43
121
 
122
+ /**
123
+ * 判断指定的字段名是否相同
124
+ * @param name - 要检查的名称
125
+ * @param fieldName - 字段名
126
+ * @returns 如果指定的名称和字段名相同,则返回 true,否则返回 false
127
+ */
44
128
  function isSameField(name: string, fieldName: string) {
45
- const { fieldName: fn } = parseGroupFieldName(name);
129
+ const { fieldName: fn } = parseGroupField(name);
46
130
 
47
131
  return fn === fieldName;
48
132
  }
@@ -1,28 +1,45 @@
1
- import { reactiveComputed } from '@vueuse/core';
1
+ /**
2
+ * TODO:
3
+ * 1. 分组表单排序
4
+ */
5
+
2
6
  import type { FormInstance, FormItemProps, RuleObject } from 'ant-design-vue/es/form';
3
7
  import type { FormProps } from 'ant-design-vue';
4
- import { computed, ref } from 'vue';
5
- import type { ComputedRef, UnwrapRef } from 'vue';
6
- import { GROUP_SEP, buildGroupFieldName, getGroupIndex } from './useAntdForm.helpers';
8
+ import { type MaybeRefOrGetter, computed, reactive, ref, toRef, watch, watchEffect } from 'vue';
9
+ import { mapValues } from 'lodash-es';
10
+ import { GROUP_SEP, buildGroupField, getGroupIndex } from './useAntdForm.helpers';
7
11
 
8
12
  export { useAntdForm };
9
- export type { TField, RecordToSchemas };
13
+ export type { SchemaConfig, ItemSchema };
14
+
15
+ type SchemaConfig<T> = {
16
+ [P in keyof T]: {
17
+ value: MaybeRefOrGetter<T[P]>
18
+ show?: (state: T) => boolean
19
+ rules?: RuleObject[] | ((state: T) => Array<RuleObject | undefined> | undefined)
20
+ }
21
+ };
22
+ type ItemSchema = SchemaConfig<any>[string];
23
+
24
+ interface Options<S, TS> {
25
+ transform?: (state: S) => TS
26
+ }
27
+
28
+ function useAntdForm<S extends Record<string, any>, TS = S>(schemas: MaybeRefOrGetter<SchemaConfig<S>>, options?: Options<S, TS>) {
29
+ const schemasR = toRef(schemas);
10
30
 
11
- function useAntdForm<
12
- F extends Record<string, TField>,
13
- S extends GetFormState<F>,
14
- STF = S,
15
- >(
16
- schemas: F,
17
- _options?: {
18
- transform?: (state: S) => STF
19
- },
20
- ) {
31
+ const state = ref({} as S);
32
+ const stateTF = computed<TS>(() => options?.transform?.(<any>state.value) ?? <any>state.value);
33
+ const show = computed(() => mapValues(state.value, (_, k): boolean => schemasR.value[k].show?.(state.value) ?? true));
34
+ const props = computed<FormProps>(() => ({ model: state.value, ref: (c: any) => (formRef.value = c) }));
35
+ const itemProps = computed(() => mapValues(state.value, (_, k): FormItemProps => {
36
+ const r = schemasR.value[k].rules;
37
+ const rules = typeof r === 'function' ? r(state.value)?.filter((i: any) => !!i) : r;
38
+
39
+ return { name: k, rules };
40
+ }));
21
41
  const formRef = ref<FormInstance>();
22
42
  const $form = {
23
- get clearValidate() {
24
- return formRef.value?.clearValidate;
25
- },
26
43
  get getFieldsValue() {
27
44
  return formRef.value?.getFieldsValue;
28
45
  },
@@ -38,64 +55,39 @@ function useAntdForm<
38
55
  get validateFields() {
39
56
  return formRef.value?.validateFields;
40
57
  },
41
- setField(name: string, schema: TField) {
42
- (schemas as any)[name] = schema;
58
+ get clearValidate() {
59
+ return formRef.value?.clearValidate;
43
60
  },
44
- addGroup(groupName: string, groupSchemas: Record<string, TField>) {
45
- const i = getGroupIndex(groupName, schemas) + 1;
61
+ addGroup(groupName: string, groupSchemas: Record<string, ItemSchema>) {
62
+ const i = getGroupIndex(groupName, schemasR.value) + 1;
46
63
 
47
64
  Object.keys(groupSchemas).forEach((key) => {
48
- const name = buildGroupFieldName(groupName, i, key);
49
- (schemas as any)[name] = groupSchemas[key];
65
+ const name = buildGroupField(groupName, i, key);
66
+ schemasR.value[name] = groupSchemas[key];
50
67
  });
51
68
  },
52
69
  removeGroup(groupName: string, index?: number) {
53
- const i = index ?? getGroupIndex(groupName, schemas);
70
+ const i = index ?? getGroupIndex(groupName, schemasR.value);
54
71
 
55
- Object.keys(schemas).forEach((key) => {
72
+ Object.keys(schemasR.value).forEach((key) => {
56
73
  if (key.startsWith(groupName + GROUP_SEP + i))
57
- Reflect.deleteProperty(schemas, key);
74
+ Reflect.deleteProperty(schemasR.value, key);
58
75
  });
59
76
  },
60
77
  };
61
- const state = reactiveComputed(() => {
62
- return Object.fromEntries(Object.entries(schemas).map(([k, v]) => [k, v.value])) as S;
63
- });
64
- const props = reactiveComputed<FormProps>(() => {
65
- return {
66
- model: state,
67
- ref: (c: any) => (formRef.value = c),
68
- };
69
- });
70
- const itemProps = reactiveComputed(() => {
71
- const propsTuple = Object.entries(schemas).map(([key, value]): [string, FormItemProps] => [
72
- key,
73
- { name: key, rules: value.rules },
74
- ]);
75
- const props = Object.fromEntries(propsTuple) as Record<keyof S, FormItemProps>;
76
78
 
77
- return props;
78
- });
79
- const stateTF = _options?.transform ? computed(() => _options.transform!(state)) : computed(() => state);
79
+ watch(itemProps, () => $form.clearValidate?.(), { flush: 'post', deep: true });
80
80
 
81
- return {
82
- $form,
83
- state,
84
- stateTF: stateTF as ComputedRef<STF>,
85
- props,
86
- itemProps,
87
- };
88
- }
81
+ watchEffect(() => {
82
+ const _state: any = {};
89
83
 
90
- interface TField<V = any> {
91
- value: V
92
- rules?: RuleObject[]
93
- }
84
+ for (const k in schemasR.value) {
85
+ const item = schemasR.value[k];
86
+ _state[k] = (k in state.value) ? toRef(state.value, k) : toRef(item, 'value');
87
+ }
94
88
 
95
- type RecordToSchemas<T extends Record<string, unknown>> = {
96
- [K in keyof T]: TField<T[K]>
97
- };
89
+ state.value = _state;
90
+ });
98
91
 
99
- type GetFormState<C extends Record<string, TField>> = {
100
- [K in keyof C]: UnwrapRef<C[K]['value']>;
101
- };
92
+ return reactive({ state, stateTF, show, props, itemProps, $form });
93
+ }