@carefrees/form-utils-vue 0.0.8
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/README.md +3 -0
- package/assets/index.css +207 -0
- package/esm/component.d.ts +3 -0
- package/esm/component.mjs +12 -0
- package/esm/form/index.d.ts +37 -0
- package/esm/form/index.mjs +6 -0
- package/esm/form/index.vue.d.ts +19 -0
- package/esm/form/index.vue.mjs +110 -0
- package/esm/form/index.vue2.mjs +4 -0
- package/esm/formItem/form.Item.base.vue.d.ts +16 -0
- package/esm/formItem/form.Item.base.vue.mjs +114 -0
- package/esm/formItem/form.Item.base.vue2.mjs +4 -0
- package/esm/formItem/form.hide.item.vue.d.ts +16 -0
- package/esm/formItem/form.hide.item.vue.mjs +78 -0
- package/esm/formItem/form.hide.item.vue2.mjs +4 -0
- package/esm/formItem/formItem.vue.d.ts +18 -0
- package/esm/formItem/formItem.vue.mjs +102 -0
- package/esm/formItem/formItem.vue2.mjs +4 -0
- package/esm/formItem/index.d.ts +71 -0
- package/esm/formItem/index.mjs +12 -0
- package/esm/hooks/attr/attr.FormItem.d.ts +876 -0
- package/esm/hooks/attr/attr.FormItem.mjs +120 -0
- package/esm/hooks/index.d.ts +13 -0
- package/esm/hooks/register/register.FormHideItem.d.ts +9 -0
- package/esm/hooks/register/register.FormHideItem.mjs +38 -0
- package/esm/hooks/register/register.FormItem.d.ts +802 -0
- package/esm/hooks/register/register.FormItem.mjs +55 -0
- package/esm/hooks/register/register.FormList.d.ts +789 -0
- package/esm/hooks/register/register.FormList.mjs +43 -0
- package/esm/hooks/register/register.form.d.ts +3 -0
- package/esm/hooks/register/register.form.mjs +12 -0
- package/esm/hooks/useAttrs.d.ts +24 -0
- package/esm/hooks/useAttrs.mjs +45 -0
- package/esm/hooks/useEffect.d.ts +2 -0
- package/esm/hooks/useEffect.mjs +14 -0
- package/esm/hooks/useForm.d.ts +8 -0
- package/esm/hooks/useForm.mjs +26 -0
- package/esm/hooks/useFormItem.d.ts +7 -0
- package/esm/hooks/useFormItem.mjs +26 -0
- package/esm/hooks/useFormItemParentName.d.ts +18 -0
- package/esm/hooks/useFormItemParentName.mjs +37 -0
- package/esm/hooks/useFormList.d.ts +7 -0
- package/esm/hooks/useFormList.mjs +26 -0
- package/esm/hooks/useHtmlFor.d.ts +2 -0
- package/esm/hooks/useHtmlFor.mjs +11 -0
- package/esm/hooks/useMultipleForm.d.ts +7 -0
- package/esm/hooks/useMultipleForm.mjs +27 -0
- package/esm/index.d.ts +11 -0
- package/esm/index.mjs +59 -0
- package/esm/instance/ruleIntsnace.d.ts +49 -0
- package/esm/instance/ruleIntsnace.mjs +73 -0
- package/esm/interface/index.d.ts +36 -0
- package/esm/interface/layout.d.ts +28 -0
- package/esm/interface/layout.formItem.d.ts +39 -0
- package/esm/layout/index.d.ts +65 -0
- package/esm/layout/index.mjs +12 -0
- package/esm/layout/layout.form.rows.vue.d.ts +10 -0
- package/esm/layout/layout.form.rows.vue.mjs +31 -0
- package/esm/layout/layout.form.rows.vue2.mjs +4 -0
- package/esm/layout/layout.formItem.vue.d.ts +18 -0
- package/esm/layout/layout.formItem.vue.mjs +216 -0
- package/esm/layout/layout.formItem.vue2.mjs +4 -0
- package/esm/layout/layout.vue.d.ts +16 -0
- package/esm/layout/layout.vue.mjs +166 -0
- package/esm/layout/layout.vue2.mjs +4 -0
- package/esm/utils/index.d.ts +1 -0
- package/esm/utils/withInstall.d.ts +3 -0
- package/esm/utils/withInstall.mjs +22 -0
- package/lib/component.d.ts +3 -0
- package/lib/component.js +12 -0
- package/lib/form/index.d.ts +37 -0
- package/lib/form/index.js +6 -0
- package/lib/form/index.vue.d.ts +19 -0
- package/lib/form/index.vue.js +110 -0
- package/lib/form/index.vue2.js +4 -0
- package/lib/formItem/form.Item.base.vue.d.ts +16 -0
- package/lib/formItem/form.Item.base.vue.js +114 -0
- package/lib/formItem/form.Item.base.vue2.js +4 -0
- package/lib/formItem/form.hide.item.vue.d.ts +16 -0
- package/lib/formItem/form.hide.item.vue.js +78 -0
- package/lib/formItem/form.hide.item.vue2.js +4 -0
- package/lib/formItem/formItem.vue.d.ts +18 -0
- package/lib/formItem/formItem.vue.js +102 -0
- package/lib/formItem/formItem.vue2.js +4 -0
- package/lib/formItem/index.d.ts +71 -0
- package/lib/formItem/index.js +12 -0
- package/lib/hooks/attr/attr.FormItem.d.ts +876 -0
- package/lib/hooks/attr/attr.FormItem.js +120 -0
- package/lib/hooks/index.d.ts +13 -0
- package/lib/hooks/register/register.FormHideItem.d.ts +9 -0
- package/lib/hooks/register/register.FormHideItem.js +38 -0
- package/lib/hooks/register/register.FormItem.d.ts +802 -0
- package/lib/hooks/register/register.FormItem.js +55 -0
- package/lib/hooks/register/register.FormList.d.ts +789 -0
- package/lib/hooks/register/register.FormList.js +43 -0
- package/lib/hooks/register/register.form.d.ts +3 -0
- package/lib/hooks/register/register.form.js +12 -0
- package/lib/hooks/useAttrs.d.ts +24 -0
- package/lib/hooks/useAttrs.js +45 -0
- package/lib/hooks/useEffect.d.ts +2 -0
- package/lib/hooks/useEffect.js +14 -0
- package/lib/hooks/useForm.d.ts +8 -0
- package/lib/hooks/useForm.js +26 -0
- package/lib/hooks/useFormItem.d.ts +7 -0
- package/lib/hooks/useFormItem.js +26 -0
- package/lib/hooks/useFormItemParentName.d.ts +18 -0
- package/lib/hooks/useFormItemParentName.js +37 -0
- package/lib/hooks/useFormList.d.ts +7 -0
- package/lib/hooks/useFormList.js +26 -0
- package/lib/hooks/useHtmlFor.d.ts +2 -0
- package/lib/hooks/useHtmlFor.js +11 -0
- package/lib/hooks/useMultipleForm.d.ts +7 -0
- package/lib/hooks/useMultipleForm.js +27 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +59 -0
- package/lib/instance/ruleIntsnace.d.ts +49 -0
- package/lib/instance/ruleIntsnace.js +73 -0
- package/lib/interface/index.d.ts +36 -0
- package/lib/interface/layout.d.ts +28 -0
- package/lib/interface/layout.formItem.d.ts +39 -0
- package/lib/layout/index.d.ts +65 -0
- package/lib/layout/index.js +12 -0
- package/lib/layout/layout.form.rows.vue.d.ts +10 -0
- package/lib/layout/layout.form.rows.vue.js +31 -0
- package/lib/layout/layout.form.rows.vue2.js +4 -0
- package/lib/layout/layout.formItem.vue.d.ts +18 -0
- package/lib/layout/layout.formItem.vue.js +216 -0
- package/lib/layout/layout.formItem.vue2.js +4 -0
- package/lib/layout/layout.vue.d.ts +16 -0
- package/lib/layout/layout.vue.js +166 -0
- package/lib/layout/layout.vue2.js +4 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/withInstall.d.ts +3 -0
- package/lib/utils/withInstall.js +22 -0
- package/package.json +33 -0
- package/src/component.ts +3 -0
- package/src/form/index.ts +3 -0
- package/src/form/index.vue +66 -0
- package/src/formItem/form.Item.base.vue +54 -0
- package/src/formItem/form.hide.item.vue +28 -0
- package/src/formItem/formItem.vue +38 -0
- package/src/formItem/index.ts +7 -0
- package/src/hooks/attr/attr.FormItem.tsx +170 -0
- package/src/hooks/index.ts +13 -0
- package/src/hooks/register/register.FormHideItem.ts +45 -0
- package/src/hooks/register/register.FormItem.ts +80 -0
- package/src/hooks/register/register.FormList.ts +49 -0
- package/src/hooks/register/register.form.ts +12 -0
- package/src/hooks/useAttrs.ts +66 -0
- package/src/hooks/useEffect.ts +13 -0
- package/src/hooks/useForm.ts +28 -0
- package/src/hooks/useFormItem.ts +28 -0
- package/src/hooks/useFormItemParentName.ts +49 -0
- package/src/hooks/useFormList.ts +28 -0
- package/src/hooks/useHtmlFor.ts +9 -0
- package/src/hooks/useMultipleForm.ts +29 -0
- package/src/index.ts +16 -0
- package/src/instance/ruleIntsnace.ts +105 -0
- package/src/interface/index.ts +40 -0
- package/src/interface/layout.formItem.ts +42 -0
- package/src/interface/layout.ts +29 -0
- package/src/layout/index.ts +8 -0
- package/src/layout/layout.form.rows.vue +19 -0
- package/src/layout/layout.formItem.vue +119 -0
- package/src/layout/layout.vue +97 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/withInstall.ts +28 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { FormItemInstanceBase } from '@carefrees/form-utils';
|
|
2
|
+
import { provide, inject, ref } from 'vue';
|
|
3
|
+
|
|
4
|
+
const formItemProvideSymbol = Symbol('carefrees-form-item');
|
|
5
|
+
|
|
6
|
+
/**表单项实例 Context */
|
|
7
|
+
export function useFormItemProvide(formItem: FormItemInstanceBase) {
|
|
8
|
+
provide(formItemProvideSymbol, formItem);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**子项中获取表单项实例*/
|
|
12
|
+
export function useFormItemInject() {
|
|
13
|
+
const formItem = inject<FormItemInstanceBase>(formItemProvideSymbol, new FormItemInstanceBase());
|
|
14
|
+
return formItem;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**s初始化 表单项实例*/
|
|
18
|
+
export function useFormItem(formItem?: FormItemInstanceBase) {
|
|
19
|
+
const refForm = ref<FormItemInstanceBase>();
|
|
20
|
+
if (!refForm.value) {
|
|
21
|
+
if (formItem) {
|
|
22
|
+
refForm.value = formItem;
|
|
23
|
+
} else {
|
|
24
|
+
refForm.value = new FormItemInstanceBase();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return refForm.value;
|
|
28
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { provide, inject, computed, ComputedRef, ref, toValue } from 'vue';
|
|
2
|
+
import { PartialComputedRefs } from '../interface';
|
|
3
|
+
|
|
4
|
+
export interface FormItemParentNamOptions {
|
|
5
|
+
/**字段*/
|
|
6
|
+
name: string;
|
|
7
|
+
/**排序*/
|
|
8
|
+
sort?: string;
|
|
9
|
+
/**是否拼接父级字段*/
|
|
10
|
+
isJoinParentField?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type FormItemParentNameProviderProps = PartialComputedRefs<Omit<FormItemParentNamOptions, 'isJoinParentField'>>;
|
|
14
|
+
|
|
15
|
+
const parentNameProvideSymbol = Symbol('carefrees-parent-name');
|
|
16
|
+
|
|
17
|
+
export const useFormItemParentNameProvide = (props: FormItemParentNameProviderProps) => {
|
|
18
|
+
const { name, sort } = props;
|
|
19
|
+
provide(
|
|
20
|
+
parentNameProvideSymbol,
|
|
21
|
+
computed(() => ({ name, sort })),
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**表单项获取父级字段*/
|
|
26
|
+
export const useFormItemParentNameInject = (options: FormItemParentNamOptions) => {
|
|
27
|
+
const { isJoinParentField = true, sort, name } = options;
|
|
28
|
+
const parentItem = inject<ComputedRef<FormItemParentNameProviderProps>>(
|
|
29
|
+
parentNameProvideSymbol,
|
|
30
|
+
computed(() => ({ name: ref(''), sort: ref('') })),
|
|
31
|
+
);
|
|
32
|
+
const newName = computed(() => {
|
|
33
|
+
const _name = toValue(parentItem.value.name);
|
|
34
|
+
if (_name && isJoinParentField) {
|
|
35
|
+
if (/^\./.test(`${name}`)) {
|
|
36
|
+
return [_name, name].filter(Boolean).join('');
|
|
37
|
+
} else if (name) {
|
|
38
|
+
return [_name, '.', name].filter(Boolean).join('');
|
|
39
|
+
}
|
|
40
|
+
return [_name, name].filter(Boolean).join('');
|
|
41
|
+
}
|
|
42
|
+
return [name].filter(Boolean).join('');
|
|
43
|
+
});
|
|
44
|
+
const newSort = computed(() => {
|
|
45
|
+
const _sort = toValue(parentItem.value.sort);
|
|
46
|
+
return [isJoinParentField ? _sort : '', sort].filter(Boolean).join('-');
|
|
47
|
+
});
|
|
48
|
+
return { newName, newSort, parentItem };
|
|
49
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { FormListInstanceBase } from '@carefrees/form-utils';
|
|
2
|
+
import { provide, inject, ref } from 'vue';
|
|
3
|
+
|
|
4
|
+
const formListProvideSymbol = Symbol('carefrees-form-list');
|
|
5
|
+
|
|
6
|
+
/**表单List实例 Context */
|
|
7
|
+
export function useFormListProvide(formList: FormListInstanceBase) {
|
|
8
|
+
provide(formListProvideSymbol, formList);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**子项中获取表单List实例*/
|
|
12
|
+
export function useFormListInject() {
|
|
13
|
+
const formList = inject<FormListInstanceBase>(formListProvideSymbol, new FormListInstanceBase());
|
|
14
|
+
return formList;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**初始化 表单List实例*/
|
|
18
|
+
export function useFormList(formList?: FormListInstanceBase) {
|
|
19
|
+
const refForm = ref<FormListInstanceBase>();
|
|
20
|
+
if (!refForm.value) {
|
|
21
|
+
if (formList) {
|
|
22
|
+
refForm.value = formList;
|
|
23
|
+
} else {
|
|
24
|
+
refForm.value = new FormListInstanceBase();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return refForm.value;
|
|
28
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ref, computed, Ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
let localId = 0;
|
|
4
|
+
export const useHtmlFor = (suffix: Ref<string>) => {
|
|
5
|
+
const count = ref(localId++);
|
|
6
|
+
return computed(() => {
|
|
7
|
+
return `carefree-vue-form-item_${count.value.toString(32)}_${suffix.value}`;
|
|
8
|
+
});
|
|
9
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { MultipleInstanceBase } from "@carefrees/form-utils"
|
|
2
|
+
import { provide, inject, ref } from "vue"
|
|
3
|
+
|
|
4
|
+
const multipleFormProvideSymbol = Symbol("carefrees-multiple-form")
|
|
5
|
+
|
|
6
|
+
/**多表单收集 Context */
|
|
7
|
+
export function useMultipleFormProvide(multipleForm?: MultipleInstanceBase) {
|
|
8
|
+
const newMultipleForm = useMultipleForm(multipleForm)
|
|
9
|
+
provide(multipleFormProvideSymbol, newMultipleForm)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**子项中获取 多表单收集 实例*/
|
|
13
|
+
export function useMultipleFormInject() {
|
|
14
|
+
const multipleForm = inject<MultipleInstanceBase>(multipleFormProvideSymbol, new MultipleInstanceBase())
|
|
15
|
+
return multipleForm
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**初始化 多表单收集 实例*/
|
|
19
|
+
export function useMultipleForm(multipleForm?: MultipleInstanceBase) {
|
|
20
|
+
const refForm = ref<MultipleInstanceBase>()
|
|
21
|
+
if (!refForm.value) {
|
|
22
|
+
if (multipleForm) {
|
|
23
|
+
refForm.value = multipleForm
|
|
24
|
+
} else {
|
|
25
|
+
refForm.value = new MultipleInstanceBase()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return refForm.value
|
|
29
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as components from './component';
|
|
2
|
+
export * from './hooks';
|
|
3
|
+
export * from './component';
|
|
4
|
+
export * from './utils';
|
|
5
|
+
export * from './interface';
|
|
6
|
+
export * from './interface/layout';
|
|
7
|
+
export * from './interface/layout.formItem';
|
|
8
|
+
|
|
9
|
+
import { App } from 'vue';
|
|
10
|
+
export default {
|
|
11
|
+
install: (app: App) => {
|
|
12
|
+
for (const c in components) {
|
|
13
|
+
app.use((components as any)[c]);
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import AsyncValidator, { RuleItem } from 'async-validator';
|
|
2
|
+
import type { MessageType, FormInstanceBase } from '@carefrees/form-utils';
|
|
3
|
+
import { ref, Ref, toValue } from 'vue';
|
|
4
|
+
|
|
5
|
+
export class RuleInstanceBase2 {
|
|
6
|
+
/**
|
|
7
|
+
* 顺序
|
|
8
|
+
* @example
|
|
9
|
+
* "0"
|
|
10
|
+
* "0-0"
|
|
11
|
+
* "0-0-0"
|
|
12
|
+
*/
|
|
13
|
+
sort?: string;
|
|
14
|
+
/**表单实例*/
|
|
15
|
+
instance?: FormInstanceBase;
|
|
16
|
+
/**
|
|
17
|
+
* 字段 ,分割方式与lodash的get和set方法值更新或设置路径一致
|
|
18
|
+
* @example
|
|
19
|
+
* 默认:"name"
|
|
20
|
+
* 嵌套字段:"name.a.doc"
|
|
21
|
+
* 嵌套字段:"name[1].a.doc"
|
|
22
|
+
* 嵌套字段:"name.a[2].doc"
|
|
23
|
+
*/
|
|
24
|
+
name: string = '';
|
|
25
|
+
/**规则*/
|
|
26
|
+
rules: Ref<RuleItem[]> = ref([]);
|
|
27
|
+
/**错误提示内容*/
|
|
28
|
+
messages: Ref<MessageType[]> = ref([]);
|
|
29
|
+
/**更新当前组件方法*/
|
|
30
|
+
updated?: Function;
|
|
31
|
+
|
|
32
|
+
/**判断是否必填*/
|
|
33
|
+
isRequired = () => {
|
|
34
|
+
if (this.instance?.getFieldHideRulesValue?.(this.name)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const findItem = (toValue(this.rules) || []).find((item) => item?.required);
|
|
38
|
+
return !!findItem;
|
|
39
|
+
};
|
|
40
|
+
/**初始化*/
|
|
41
|
+
ctor = (name: string, rules: RuleItem[]) => {
|
|
42
|
+
this.name = name;
|
|
43
|
+
this.rules.value = rules || [];
|
|
44
|
+
return this;
|
|
45
|
+
};
|
|
46
|
+
/**判断是否需要验证*/
|
|
47
|
+
isValidate = () => {
|
|
48
|
+
if (this.instance?.getFieldHideRulesValue?.(this.name)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const _rules = toValue(this.rules);
|
|
52
|
+
return !!(Array.isArray(_rules) && _rules.length);
|
|
53
|
+
};
|
|
54
|
+
/**更新提示信息*/
|
|
55
|
+
updatedMessages = (messages?: MessageType[]) => {
|
|
56
|
+
this.messages.value = messages || [];
|
|
57
|
+
this.updated?.({});
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**更新规则*/
|
|
61
|
+
updatedRules = (rules: RuleItem[]) => {
|
|
62
|
+
/**更新当前规则*/
|
|
63
|
+
this.rules.value = rules;
|
|
64
|
+
/**当前组件重新渲染*/
|
|
65
|
+
this.updatedMessages?.([]);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**验证规则
|
|
69
|
+
* @param {boolean} isOnly 仅判断是否校验通过
|
|
70
|
+
*/
|
|
71
|
+
validate = (isOnly: boolean = false) => {
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
const value = this.instance?.getFieldValue?.(this.name);
|
|
74
|
+
if (this.instance?.getFieldHideRulesValue?.(this.name)) {
|
|
75
|
+
this.updatedMessages([]);
|
|
76
|
+
resolve({ [this.name]: value });
|
|
77
|
+
}
|
|
78
|
+
new AsyncValidator({ [this.name]: toValue(this.rules) || [] })
|
|
79
|
+
.validate({ [this.name]: value })
|
|
80
|
+
.then((values) => {
|
|
81
|
+
if (!isOnly) this.updatedMessages([]);
|
|
82
|
+
resolve(values);
|
|
83
|
+
})
|
|
84
|
+
.catch(({ errors }) => {
|
|
85
|
+
if (Array.isArray(errors)) {
|
|
86
|
+
if (!isOnly) this.updatedMessages(errors);
|
|
87
|
+
reject(errors);
|
|
88
|
+
} else {
|
|
89
|
+
reject();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**获取校验结果*/
|
|
96
|
+
getValidateResult = () => {
|
|
97
|
+
const _messages = toValue(this.messages);
|
|
98
|
+
const tip = Array.isArray(_messages) ? _messages.map((it) => it.message) : '';
|
|
99
|
+
const isInvalid = Array.isArray(tip) ? !!tip.length : !!tip;
|
|
100
|
+
return {
|
|
101
|
+
tip,
|
|
102
|
+
isInvalid,
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ComputedRef, Ref, VNodeChild, StyleValue, UnwrapNestedRefs } from 'vue';
|
|
2
|
+
import { FormItemAttrOptions } from '../hooks/attr/attr.FormItem';
|
|
3
|
+
import { LayoutFormItemProps } from './layout.formItem';
|
|
4
|
+
import { FormLayoutProps } from './layout';
|
|
5
|
+
import { FormInstanceBase, ValidateErrorEntity } from '@carefrees/form-utils';
|
|
6
|
+
|
|
7
|
+
export type ComputedRefBase<T> = ComputedRef<T> | Ref<T>;
|
|
8
|
+
|
|
9
|
+
export type PartialComputedRefs<T> = {
|
|
10
|
+
[K in keyof T]: ComputedRefBase<T[K]>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export interface FormItemProps extends FormItemAttrOptions, LayoutFormItemProps {
|
|
14
|
+
/**不进行样式渲染*/
|
|
15
|
+
noStyle?: boolean;
|
|
16
|
+
/**输入框组件*/
|
|
17
|
+
input?: VNodeChild;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface FormProps<T = any> extends FormLayoutProps {
|
|
21
|
+
form?: FormInstanceBase;
|
|
22
|
+
style?: StyleValue;
|
|
23
|
+
class?: string;
|
|
24
|
+
layoutClass?: string;
|
|
25
|
+
layoutStyle?: StyleValue;
|
|
26
|
+
/**表单数据*/
|
|
27
|
+
formData?: any;
|
|
28
|
+
/**值更新触发*/
|
|
29
|
+
onValuesChange?: (changedValues: Partial<T>, values: UnwrapNestedRefs<T>) => void;
|
|
30
|
+
/**提交保存 验证成功*/
|
|
31
|
+
onFinish?: (values: T) => void;
|
|
32
|
+
/**提交保存 验证失败*/
|
|
33
|
+
onFinishFailed?: (errorInfo: ValidateErrorEntity<T>) => void;
|
|
34
|
+
/**隐藏表单项初始值*/
|
|
35
|
+
hideData?: Record<string, boolean>;
|
|
36
|
+
/**表单名称*/
|
|
37
|
+
name?: string;
|
|
38
|
+
/**隐藏规则校验*/
|
|
39
|
+
hideRuleData?: Record<string, boolean>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { VNodeChild, StyleValue } from 'vue';
|
|
2
|
+
import { ComputedRefBase } from '.';
|
|
3
|
+
|
|
4
|
+
export interface LayoutFormItemProps {
|
|
5
|
+
/**规则校验失败错误提示位置*/
|
|
6
|
+
errorLayout?: 'left-bottom' | 'right-bottom' | 'top-right' | 'top-left';
|
|
7
|
+
/**必填样式*/
|
|
8
|
+
required?: boolean;
|
|
9
|
+
/**label显示模式*/
|
|
10
|
+
labelMode?: 'left' | 'top' | 'between' | 'hide';
|
|
11
|
+
/**只进行规则样式*/
|
|
12
|
+
onlyRuleStyle?: boolean;
|
|
13
|
+
label?: VNodeChild;
|
|
14
|
+
/**底部提示内容*/
|
|
15
|
+
helpText?: VNodeChild;
|
|
16
|
+
/**额外内容*/
|
|
17
|
+
extra?: VNodeChild;
|
|
18
|
+
/**是否显示label后的冒号*/
|
|
19
|
+
showColon?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* 表单项占据列数
|
|
22
|
+
* @default 1
|
|
23
|
+
*/
|
|
24
|
+
colSpan?: number;
|
|
25
|
+
/**
|
|
26
|
+
* 表单项占据行数
|
|
27
|
+
* @default 1
|
|
28
|
+
*/
|
|
29
|
+
rowSpan?: number;
|
|
30
|
+
|
|
31
|
+
htmlFor?: ComputedRefBase<string>;
|
|
32
|
+
/**规则验证结果*/
|
|
33
|
+
validateResult?: ComputedRefBase<{
|
|
34
|
+
tip: string | (string | undefined)[];
|
|
35
|
+
isInvalid: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
// 样式部分
|
|
38
|
+
style?: StyleValue;
|
|
39
|
+
class?: string;
|
|
40
|
+
labelStyle?: StyleValue;
|
|
41
|
+
labelClass?: string;
|
|
42
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AttrsOptions } from '../hooks/useAttrs';
|
|
2
|
+
import { VNodeChild, StyleValue } from 'vue';
|
|
3
|
+
|
|
4
|
+
export interface FormLayoutProps extends Omit<AttrsOptions, 'colCount'> {
|
|
5
|
+
/**标题*/
|
|
6
|
+
title?: VNodeChild;
|
|
7
|
+
/**额外内容*/
|
|
8
|
+
extra?: VNodeChild;
|
|
9
|
+
/**是否占据整行*/
|
|
10
|
+
isAllColSpan?: boolean;
|
|
11
|
+
class?: string;
|
|
12
|
+
/**头部ClassName*/
|
|
13
|
+
headerClass?: string;
|
|
14
|
+
/**内容Class*/
|
|
15
|
+
bodyClass?: string;
|
|
16
|
+
style?: StyleValue;
|
|
17
|
+
/**头部样式*/
|
|
18
|
+
headerStyle?: StyleValue;
|
|
19
|
+
/**内容样式*/
|
|
20
|
+
bodyStyle?: StyleValue;
|
|
21
|
+
/**是否添加边框*/
|
|
22
|
+
bordered?: boolean;
|
|
23
|
+
/**列数据*/
|
|
24
|
+
colCount?: number;
|
|
25
|
+
/**
|
|
26
|
+
* @description gap 属性是用来设置网格行与列之间的间隙,该属性是row-gap and column-gap的简写形式。
|
|
27
|
+
*/
|
|
28
|
+
gap?: string | number;
|
|
29
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import _Layout from './layout.vue';
|
|
2
|
+
import _LayoutFormItem from './layout.formItem.vue';
|
|
3
|
+
import _FormLayoutRows from './layout.form.rows.vue';
|
|
4
|
+
import { withInstall } from '../utils';
|
|
5
|
+
|
|
6
|
+
export const Layout = withInstall(_Layout);
|
|
7
|
+
export const LayoutFormItem = withInstall(_LayoutFormItem);
|
|
8
|
+
export const FormLayoutRows = withInstall(_FormLayoutRows);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-bind='props' :class='cls'>
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script lang="ts" setup>
|
|
8
|
+
import clx from 'classnames';
|
|
9
|
+
import { defineProps, withDefaults, computed, HTMLAttributes } from "vue"
|
|
10
|
+
const preCls = 'carefrees-form-layout';
|
|
11
|
+
defineOptions({
|
|
12
|
+
name: 'FormLayoutRows',
|
|
13
|
+
inheritAttrs: false, // 可选,防止属性自动应用到根元素
|
|
14
|
+
});
|
|
15
|
+
const props = withDefaults(defineProps</* @vue-ignore */HTMLAttributes>(), {})
|
|
16
|
+
const cls = computed(() => clx(preCls, props.class, {
|
|
17
|
+
'all-colspan': true,
|
|
18
|
+
}))
|
|
19
|
+
</script>
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class='cls' :style='style'>
|
|
3
|
+
<div :class='containerCls'>
|
|
4
|
+
<template v-if='_isLabel'>
|
|
5
|
+
<div :class='labelWarpCls'>
|
|
6
|
+
<label :style='labelStyle' :class='labelCls' :for='toValue(props.htmlFor)'>
|
|
7
|
+
<template v-if='props.label'>
|
|
8
|
+
{{ props.label }}
|
|
9
|
+
</template>
|
|
10
|
+
<template v-else>
|
|
11
|
+
<slot name='label' />
|
|
12
|
+
</template>
|
|
13
|
+
</label>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
<div :class='preCls + "-body"'>
|
|
17
|
+
<div :class='inputCls'>
|
|
18
|
+
<slot />
|
|
19
|
+
</div>
|
|
20
|
+
<template v-if='isHelpText'>
|
|
21
|
+
<div :class='preCls + "-body-help"'>
|
|
22
|
+
<template v-if='props.helpText'>
|
|
23
|
+
{{ props.helpText }}
|
|
24
|
+
</template>
|
|
25
|
+
<template v-else>
|
|
26
|
+
<slot name='helpText' />
|
|
27
|
+
</template>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
<template v-if='!!tips'>
|
|
31
|
+
<div :class='errorCls'>{{ tips }}</div>
|
|
32
|
+
</template>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<template v-if='isExtra'>
|
|
36
|
+
<div :class='preCls + "-extra"'>
|
|
37
|
+
<template v-if='props.extra'>
|
|
38
|
+
{{ props.extra }}
|
|
39
|
+
</template>
|
|
40
|
+
<template v-else>
|
|
41
|
+
<slot name='extra' />
|
|
42
|
+
</template>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<script lang="ts" setup>
|
|
49
|
+
import clx from 'classnames';
|
|
50
|
+
import { defineProps, withDefaults, toValue, computed, StyleValue, watch } from "vue"
|
|
51
|
+
import type { LayoutFormItemProps } from "../interface/layout.formItem"
|
|
52
|
+
import { useAttrsInject } from "../hooks/useAttrs"
|
|
53
|
+
const preCls = 'carefrees-form-item';
|
|
54
|
+
defineOptions({
|
|
55
|
+
name: 'LayoutFormItem',
|
|
56
|
+
inheritAttrs: false, // 可选,防止属性自动应用到根元素
|
|
57
|
+
});
|
|
58
|
+
const attrs = useAttrsInject();
|
|
59
|
+
const props = withDefaults(defineProps<LayoutFormItemProps>(), {})
|
|
60
|
+
|
|
61
|
+
const solts = defineSlots<{
|
|
62
|
+
default: any,
|
|
63
|
+
label: any,
|
|
64
|
+
helpText: any,
|
|
65
|
+
extra: any,
|
|
66
|
+
}>()
|
|
67
|
+
|
|
68
|
+
const labelMode = computed(() => props.labelMode ?? toValue(attrs.value.labelMode) ?? 'top')
|
|
69
|
+
const showColon = computed(() => props.showColon ?? toValue(attrs.value.showColon))
|
|
70
|
+
const errorLayout = computed(() => props.errorLayout ?? toValue(attrs.value.errorLayout) ?? "left-bottom")
|
|
71
|
+
const tips = computed(() => {
|
|
72
|
+
const v = toValue(props.validateResult)?.tip
|
|
73
|
+
if (Array.isArray(v)) {
|
|
74
|
+
return v.join(',')
|
|
75
|
+
}
|
|
76
|
+
return v
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const cls = computed(() => clx(preCls, props.class, toValue(attrs.value.formItemClass), { 'dx-invalid': !!toValue(props.validateResult)?.isInvalid }))
|
|
80
|
+
const containerCls = computed(() => clx(`${preCls}-container`, { [`${labelMode.value}`]: !!labelMode.value }));
|
|
81
|
+
const labelCls = computed(() => clx(`${preCls}-label`, { required: !!props.required, 'show-colon': showColon && (labelMode.value === 'left' || labelMode.value === 'between'), },));
|
|
82
|
+
const labelWarpCls = computed(() => clx(`${preCls}-label-warp`, toValue(props.labelClass), toValue(attrs.value.formItemLabelClass)));
|
|
83
|
+
const inputCls = computed(() => clx(`${preCls}-body-input`));
|
|
84
|
+
const errorCls = computed(() => clx(`${preCls}-body-error`, { [errorLayout.value]: !!errorLayout.value }));
|
|
85
|
+
const _isLabel = computed(() => (props.label || solts.label) && labelMode.value !== 'hide');
|
|
86
|
+
|
|
87
|
+
const style = computed(() => {
|
|
88
|
+
const css: StyleValue = {};
|
|
89
|
+
|
|
90
|
+
if (props.onlyRuleStyle) {
|
|
91
|
+
css.padding = '0px';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (props.colSpan) {
|
|
95
|
+
const colCount = toValue(attrs.value.colCount) || 4;
|
|
96
|
+
const end = colCount > props.colSpan ? props.colSpan : colCount;
|
|
97
|
+
css.gridColumnEnd = `span ${end}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (props.rowSpan) {
|
|
101
|
+
css.gridRowEnd = `span ${props.rowSpan}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return [toValue(attrs.value.formItemStyle), props.style, css]
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const labelStyle = computed(() => {
|
|
108
|
+
return [toValue(attrs.value.formItemLabelStyle), props.labelStyle]
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
const isHelpText = computed(() => {
|
|
112
|
+
return !!props.helpText || solts.helpText;
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const isExtra = computed(() => {
|
|
116
|
+
return !!props.extra || solts.extra;
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
</script>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :style='props.style' :class='cls'>
|
|
3
|
+
<template v-if="isExtra || isTitle">
|
|
4
|
+
<div :style='headerStyle' :class='headerCls'>
|
|
5
|
+
<div :class='headerTitleCls'>
|
|
6
|
+
<template v-if='props.title'>
|
|
7
|
+
{{ props.title }}
|
|
8
|
+
</template>
|
|
9
|
+
<template v-else>
|
|
10
|
+
<slot name='title' />
|
|
11
|
+
</template>
|
|
12
|
+
</div>
|
|
13
|
+
<div :class='headerExtraCls'>
|
|
14
|
+
<template v-if='props.extra'>
|
|
15
|
+
{{ props.extra }}
|
|
16
|
+
</template>
|
|
17
|
+
<template v-else>
|
|
18
|
+
<slot name='extra' />
|
|
19
|
+
</template>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
<div :style='bodyStyle' :class='bodyCls'>
|
|
24
|
+
<slot />
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script lang="ts" setup>
|
|
30
|
+
import clx from 'classnames';
|
|
31
|
+
import { defineProps, withDefaults, toValue, computed, StyleValue } from "vue"
|
|
32
|
+
import type { FormLayoutProps } from "../interface/layout"
|
|
33
|
+
import { useAttrsInject, useAttrsProvide } from "../hooks/useAttrs"
|
|
34
|
+
const preCls = 'carefrees-form-layout';
|
|
35
|
+
|
|
36
|
+
const attrs = useAttrsInject();
|
|
37
|
+
const props = withDefaults(defineProps<FormLayoutProps>(), {})
|
|
38
|
+
|
|
39
|
+
defineOptions({
|
|
40
|
+
name: 'Layout',
|
|
41
|
+
inheritAttrs: false, // 可选,防止属性自动应用到根元素
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const solts = defineSlots<{
|
|
45
|
+
default: any,
|
|
46
|
+
title: any,
|
|
47
|
+
extra: any,
|
|
48
|
+
}>()
|
|
49
|
+
|
|
50
|
+
const colCount = computed(() => toValue(props.colCount) ?? toValue(attrs.value.colCount))
|
|
51
|
+
const errorLayout = computed(() => toValue(props.errorLayout) ?? toValue(attrs.value.errorLayout))
|
|
52
|
+
const labelMode = computed(() => toValue(props.labelMode) ?? toValue(attrs.value.labelMode))
|
|
53
|
+
const showColon = computed(() => toValue(props.showColon) ?? toValue(attrs.value.showColon))
|
|
54
|
+
const formItemClass = computed(() => toValue(props.formItemClass) ?? toValue(attrs.value.formItemClass))
|
|
55
|
+
const formItemStyle = computed(() => toValue(props.formItemStyle) ?? toValue(attrs.value.formItemStyle))
|
|
56
|
+
const formItemLabelClass = computed(() => toValue(props.formItemLabelClass) ?? toValue(attrs.value.formItemLabelClass))
|
|
57
|
+
const formItemLabelStyle = computed(() => toValue(props.formItemLabelStyle) ?? toValue(attrs.value.formItemLabelStyle))
|
|
58
|
+
|
|
59
|
+
useAttrsProvide({
|
|
60
|
+
colCount,
|
|
61
|
+
errorLayout,
|
|
62
|
+
labelMode,
|
|
63
|
+
showColon,
|
|
64
|
+
formItemClass,
|
|
65
|
+
formItemStyle,
|
|
66
|
+
formItemLabelClass,
|
|
67
|
+
formItemLabelStyle
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const cls = computed(() => clx(preCls, props.class, { 'all-colspan': props.isAllColSpan, bordered: props.bordered, }));
|
|
71
|
+
const bodyCls = computed(() => clx(`${preCls}-body`, props.bodyClass));
|
|
72
|
+
const headerCls = computed(() => clx(`${preCls}-header`, props.headerClass));
|
|
73
|
+
const headerTitleCls = computed(() => clx(`${preCls}-header-title`));
|
|
74
|
+
const headerExtraCls = computed(() => clx(`${preCls}-header-extra`));
|
|
75
|
+
|
|
76
|
+
const bodyStyle = computed(() => {
|
|
77
|
+
const css: StyleValue = {};
|
|
78
|
+
if (typeof props.gap === 'string') {
|
|
79
|
+
css.gap = props.gap;
|
|
80
|
+
}
|
|
81
|
+
if (typeof props.gap === 'number') {
|
|
82
|
+
css.gap = `${props.gap}px`;
|
|
83
|
+
}
|
|
84
|
+
if (colCount.value) {
|
|
85
|
+
css.gridTemplateColumns = `repeat(${colCount.value}, auto)`;
|
|
86
|
+
}
|
|
87
|
+
return [css, props.bodyStyle];
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const isTitle = computed(() => {
|
|
91
|
+
return !!props.title || solts.title;
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const isExtra = computed(() => {
|
|
95
|
+
return !!props.extra || solts.extra;
|
|
96
|
+
})
|
|
97
|
+
</script>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './withInstall';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
import type { Plugin, App } from 'vue';
|
|
3
|
+
export type SFCWithInstall<T> = T & Plugin;
|
|
4
|
+
export const withInstall = <T, E extends Record<string, any> = Record<string, any>, K extends Record<string, any> = Record<string, any>>(
|
|
5
|
+
main: T,
|
|
6
|
+
extra?: E,// 挂 hooks 之类的
|
|
7
|
+
childComponents?: K,// 挂子组件
|
|
8
|
+
) => {
|
|
9
|
+
; (main as SFCWithInstall<T>).install = (app: App): void => {
|
|
10
|
+
for (const comp of [main, ...Object.values(childComponents ?? {})]) {
|
|
11
|
+
// 当组件是 script setup 的形式时,会自动以为文件名注册,会挂载到组件的__name 属性上
|
|
12
|
+
// 所以要加上这个条件
|
|
13
|
+
const name = (comp as any).name || (comp as any).__name;
|
|
14
|
+
app.component(name, comp)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (childComponents) {
|
|
18
|
+
for (const [key, comp] of Object.entries(childComponents)) {
|
|
19
|
+
; (main as any)[key] = comp
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (extra) {
|
|
23
|
+
for (const [key, comp] of Object.entries(extra)) {
|
|
24
|
+
; (main as any)[key] = comp
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return main as SFCWithInstall<T> & E & K
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|