提交 4ff1c408 作者: vben

wip(form): perf form

上级 3c3e640d
...@@ -48,11 +48,11 @@ ...@@ -48,11 +48,11 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^11.0.0", "@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0", "@commitlint/config-conventional": "^11.0.0",
"@iconify/json": "^1.1.275", "@iconify/json": "^1.1.276",
"@ls-lint/ls-lint": "^1.9.2", "@ls-lint/ls-lint": "^1.9.2",
"@purge-icons/generated": "^0.4.1", "@purge-icons/generated": "^0.4.1",
"@types/echarts": "^4.9.3", "@types/echarts": "^4.9.3",
"@types/fs-extra": "^9.0.5", "@types/fs-extra": "^9.0.6",
"@types/globrex": "^0.1.0", "@types/globrex": "^0.1.0",
"@types/koa-static": "^4.0.1", "@types/koa-static": "^4.0.1",
"@types/lodash-es": "^4.17.4", "@types/lodash-es": "^4.17.4",
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
"vite-plugin-html": "^1.0.0-beta.2", "vite-plugin-html": "^1.0.0-beta.2",
"vite-plugin-mock": "^1.0.9", "vite-plugin-mock": "^1.0.9",
"vite-plugin-purge-icons": "^0.4.5", "vite-plugin-purge-icons": "^0.4.5",
"vite-plugin-pwa": "^0.1.7", "vite-plugin-pwa": "^0.2.0",
"vue-eslint-parser": "^7.3.0", "vue-eslint-parser": "^7.3.0",
"yargs": "^16.2.0" "yargs": "^16.2.0"
}, },
......
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
import BasicArrow from './src/BasicArrow.vue';
export const BasicArrow = createAsyncComponent(() => import('./src/BasicArrow.vue')); export { BasicArrow };
// export const BasicArrow = createAsyncComponent(() => import('./src/BasicArrow.vue'));
export const BasicHelp = createAsyncComponent(() => import('./src/BasicHelp.vue')); export const BasicHelp = createAsyncComponent(() => import('./src/BasicHelp.vue'));
export const BasicTitle = createAsyncComponent(() => import('./src/BasicTitle.vue')); export const BasicTitle = createAsyncComponent(() => import('./src/BasicTitle.vue'));
...@@ -101,7 +101,10 @@ ...@@ -101,7 +101,10 @@
&__action { &__action {
display: flex; display: flex;
text-align: right;
flex: 1;
align-items: center; align-items: center;
justify-content: flex-end;
} }
} }
</style> </style>
<template> <template>
<Form v-bind="{ ...$attrs, ...$props }" ref="formElRef" :model="formModel"> <Form v-bind="{ ...$attrs, ...$props }" ref="formElRef" :model="formModel">
<Row :class="getProps.compact ? 'compact-form-row' : ''" :style="getRowWrapStyleRef"> <Row :class="getProps.compact ? 'compact-form-row' : ''" :style="getRowWrapStyle">
<slot name="formHeader" /> <slot name="formHeader" />
<template v-for="schema in getSchema" :key="schema.field"> <template v-for="schema in getSchema" :key="schema.field">
<FormItem <FormItem
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
:formProps="getProps" :formProps="getProps"
:allDefaultValues="defaultValueRef" :allDefaultValues="defaultValueRef"
:formModel="formModel" :formModel="formModel"
:setFormModel="setFormModel"
> >
<template #[item]="data" v-for="item in Object.keys($slots)"> <template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data" /> <slot :name="item" v-bind="data" />
...@@ -17,8 +18,9 @@ ...@@ -17,8 +18,9 @@
</FormItem> </FormItem>
</template> </template>
<!-- -->
<FormAction <FormAction
v-bind="{ ...getActionPropsRef, ...advanceState }" v-bind="{ ...getProps, ...advanceState }"
@toggle-advanced="handleToggleAdvanced" @toggle-advanced="handleToggleAdvanced"
/> />
<slot name="formFooter" /> <slot name="formFooter" />
...@@ -28,14 +30,12 @@ ...@@ -28,14 +30,12 @@
<script lang="ts"> <script lang="ts">
import type { FormActionType, FormProps, FormSchema } from './types/form'; import type { FormActionType, FormProps, FormSchema } from './types/form';
import type { AdvanceState } from './types/hooks'; import type { AdvanceState } from './types/hooks';
import type { Ref, WatchStopHandle } from 'vue'; import type { CSSProperties, Ref, WatchStopHandle } from 'vue';
import type { ValidateFields } from 'ant-design-vue/lib/form/interface';
import { defineComponent, reactive, ref, computed, unref, onMounted, watch, toRefs } from 'vue'; import { defineComponent, reactive, ref, computed, unref, onMounted, watch, toRefs } from 'vue';
import { Form, Row } from 'ant-design-vue'; import { Form, Row } from 'ant-design-vue';
import FormItem from './FormItem'; import FormItem from './components/FormItem';
import { basicProps } from './props'; import FormAction from './components/FormAction.vue';
import FormAction from './FormAction';
import { dateItemType } from './helper'; import { dateItemType } from './helper';
import moment from 'moment'; import moment from 'moment';
...@@ -44,7 +44,11 @@ ...@@ -44,7 +44,11 @@
import { useFormValues } from './hooks/useFormValues'; import { useFormValues } from './hooks/useFormValues';
import useAdvanced from './hooks/useAdvanced'; import useAdvanced from './hooks/useAdvanced';
import { useFormAction } from './hooks/useFormAction'; import { useFormEvents } from './hooks/useFormEvents';
import { createFormContext } from './hooks/useFormContext';
import { basicProps } from './props';
export default defineComponent({ export default defineComponent({
name: 'BasicForm', name: 'BasicForm',
components: { FormItem, Form, Row, FormAction }, components: { FormItem, Form, Row, FormAction },
...@@ -52,12 +56,7 @@ ...@@ -52,12 +56,7 @@
props: basicProps, props: basicProps,
emits: ['advanced-change', 'reset', 'submit', 'register'], emits: ['advanced-change', 'reset', 'submit', 'register'],
setup(props, { emit }) { setup(props, { emit }) {
const formModel = reactive({}); const formModel = reactive<Recordable>({});
const actionState = reactive({
resetAction: {},
submitAction: {},
});
const advanceState = reactive<AdvanceState>({ const advanceState = reactive<AdvanceState>({
isAdvanced: true, isAdvanced: true,
...@@ -66,37 +65,24 @@ ...@@ -66,37 +65,24 @@
actionSpan: 6, actionSpan: 6,
}); });
const defaultValueRef = ref<any>({}); const defaultValueRef = ref<Recordable>({});
const isInitedDefaultRef = ref(false); const isInitedDefaultRef = ref(false);
const propsRef = ref<Partial<FormProps>>({}); const propsRef = ref<Partial<FormProps>>({});
const schemaRef = ref<Nullable<FormSchema[]>>(null); const schemaRef = ref<Nullable<FormSchema[]>>(null);
const formElRef = ref<Nullable<FormActionType>>(null); const formElRef = ref<Nullable<FormActionType>>(null);
const getMergePropsRef = computed( // Get the basic configuration of the form
const getProps = computed(
(): FormProps => { (): FormProps => {
return deepMerge(cloneDeep(props), unref(propsRef)); return deepMerge(cloneDeep(props), unref(propsRef));
} }
); );
const getRowWrapStyleRef = computed((): any => { // Get uniform row style
const { baseRowStyle } = unref(getMergePropsRef); const getRowWrapStyle = computed(
return baseRowStyle || {}; (): CSSProperties => {
}); const { baseRowStyle = {} } = unref(getProps);
return baseRowStyle;
// 获取表单基本配置
const getProps = computed(
(): FormProps => {
return {
...unref(getMergePropsRef),
resetButtonOptions: deepMerge(
actionState.resetAction,
unref(getMergePropsRef).resetButtonOptions || {}
),
submitButtonOptions: deepMerge(
actionState.submitAction,
unref(getMergePropsRef).submitButtonOptions || {}
),
};
} }
); );
...@@ -120,18 +106,19 @@ ...@@ -120,18 +106,19 @@
return schemas as FormSchema[]; return schemas as FormSchema[];
}); });
const { getActionPropsRef, handleToggleAdvanced } = useAdvanced({ const { handleToggleAdvanced } = useAdvanced({
advanceState, advanceState,
emit, emit,
getMergePropsRef,
getProps, getProps,
getSchema, getSchema,
formModel, formModel,
defaultValueRef, defaultValueRef,
}); });
const { transformDateFunc, fieldMapToTime } = toRefs(props); const { transformDateFunc, fieldMapToTime } = toRefs(props);
const { handleFormValues, initDefault } = useFormValues({ const { handleFormValues, initDefault } = useFormValues({
transformDateFuncRef: transformDateFunc as Ref<Fn<any>>, transformDateFuncRef: transformDateFunc,
fieldMapToTimeRef: fieldMapToTime, fieldMapToTimeRef: fieldMapToTime,
defaultValueRef, defaultValueRef,
getSchema, getSchema,
...@@ -139,7 +126,7 @@ ...@@ -139,7 +126,7 @@
}); });
const { const {
// handleSubmit, handleSubmit,
setFieldsValue, setFieldsValue,
clearValidate, clearValidate,
validate, validate,
...@@ -149,7 +136,8 @@ ...@@ -149,7 +136,8 @@
appendSchemaByField, appendSchemaByField,
removeSchemaByFiled, removeSchemaByFiled,
resetFields, resetFields,
} = useFormAction({ scrollToField,
} = useFormEvents({
emit, emit,
getProps, getProps,
formModel, formModel,
...@@ -158,14 +146,19 @@ ...@@ -158,14 +146,19 @@
formElRef: formElRef as Ref<FormActionType>, formElRef: formElRef as Ref<FormActionType>,
schemaRef: schemaRef as Ref<FormSchema[]>, schemaRef: schemaRef as Ref<FormSchema[]>,
handleFormValues, handleFormValues,
actionState, });
createFormContext({
resetAction: resetFields,
submitAction: handleSubmit,
}); });
watch( watch(
() => unref(getMergePropsRef).model, () => unref(getProps).model,
() => { () => {
if (!unref(getMergePropsRef).model) return; const { model } = unref(getProps);
setFieldsValue(unref(getMergePropsRef).model); if (!model) return;
setFieldsValue(model);
}, },
{ {
immediate: true, immediate: true,
...@@ -178,16 +171,19 @@ ...@@ -178,16 +171,19 @@
if (unref(isInitedDefaultRef)) { if (unref(isInitedDefaultRef)) {
return stopWatch(); return stopWatch();
} }
if (schema && schema.length) { if (schema?.length) {
initDefault(); initDefault();
isInitedDefaultRef.value = true; isInitedDefaultRef.value = true;
} }
} }
); );
function setProps(formProps: Partial<FormProps>): void { async function setProps(formProps: Partial<FormProps>): Promise<void> {
const mergeProps = deepMerge(unref(propsRef) || {}, formProps); propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
propsRef.value = mergeProps; }
function setFormModel(key: string, value: any) {
formModel[key] = value;
} }
const formActionType: Partial<FormActionType> = { const formActionType: Partial<FormActionType> = {
...@@ -199,8 +195,10 @@ ...@@ -199,8 +195,10 @@
removeSchemaByFiled, removeSchemaByFiled,
appendSchemaByField, appendSchemaByField,
clearValidate, clearValidate,
validateFields: validateFields as ValidateFields, validateFields,
validate: validate as ValidateFields, validate,
submit: handleSubmit,
scrollToField: scrollToField,
}; };
onMounted(() => { onMounted(() => {
...@@ -211,14 +209,14 @@ ...@@ -211,14 +209,14 @@
return { return {
handleToggleAdvanced, handleToggleAdvanced,
formModel, formModel,
getActionPropsRef,
defaultValueRef, defaultValueRef,
advanceState, advanceState,
getRowWrapStyleRef, getRowWrapStyle,
getProps, getProps,
formElRef, formElRef,
getSchema, getSchema,
formActionType, formActionType,
setFormModel,
...formActionType, ...formActionType,
}; };
}, },
......
import type { ColEx } from './types/index';
import { defineComponent, unref, computed, PropType } from 'vue';
import { Form, Col } from 'ant-design-vue';
import { Button } from '/@/components/Button';
import { BasicArrow } from '/@/components/Basic/index';
import { getSlot } from '/@/utils/helper/tsxHelper';
import { useI18n } from '/@/hooks/web/useI18n';
import { propTypes } from '/@/utils/propTypes';
const { t } = useI18n();
export default defineComponent({
name: 'BasicFormAction',
props: {
show: propTypes.bool.def(true),
showResetButton: propTypes.bool.def(true),
showSubmitButton: propTypes.bool.def(true),
showAdvancedButton: propTypes.bool.def(true),
resetButtonOptions: {
type: Object as PropType<any>,
default: {},
},
submitButtonOptions: {
type: Object as PropType<any>,
default: {},
},
actionColOptions: {
type: Object as PropType<any>,
default: {},
},
actionSpan: propTypes.number.def(6),
isAdvanced: propTypes.bool,
hideAdvanceBtn: propTypes.bool,
},
emits: ['toggle-advanced'],
setup(props, { slots, emit }) {
const getResetBtnOptionsRef = computed(() => {
return {
text: t('component.form.resetButton'),
...props.resetButtonOptions,
};
});
const getSubmitBtnOptionsRef = computed(() => {
return {
text: t('component.form.submitButton'),
// htmlType: 'submit',
...props.submitButtonOptions,
};
});
const actionColOpt = computed(() => {
const { showAdvancedButton, actionSpan: span, actionColOptions } = props;
const actionSpan = 24 - span;
const advancedSpanObj = showAdvancedButton ? { span: actionSpan < 6 ? 24 : actionSpan } : {};
const actionColOpt: Partial<ColEx> = {
span: showAdvancedButton ? 6 : 4,
...advancedSpanObj,
...actionColOptions,
};
return actionColOpt;
});
function toggleAdvanced() {
emit('toggle-advanced');
}
function renderAdvanceButton() {
const { showAdvancedButton, hideAdvanceBtn, isAdvanced } = props;
if (!showAdvancedButton || !!hideAdvanceBtn) {
return null;
}
return (
<Button type="default" class="mr-2" onClick={toggleAdvanced}>
{() => (
<>
{isAdvanced ? t('component.form.putAway') : t('component.form.unfold')}
<BasicArrow expand={!isAdvanced} top />
</>
)}
</Button>
);
}
function renderResetButton() {
const { showResetButton } = props;
if (!showResetButton) {
return null;
}
return (
<Button type="default" class="mr-2" {...unref(getResetBtnOptionsRef)}>
{() => unref(getResetBtnOptionsRef).text}
</Button>
);
}
function renderSubmitButton() {
const { showSubmitButton } = props;
if (!showSubmitButton) {
return null;
}
return (
<Button type="primary" {...unref(getSubmitBtnOptionsRef)}>
{() => unref(getSubmitBtnOptionsRef).text}
</Button>
);
}
return () => {
if (!props.show) {
return null;
}
return (
<Col {...unref(actionColOpt)} style={{ textAlign: 'right' }}>
{() => (
<Form.Item>
{() => (
<>
{getSlot(slots, 'advanceBefore')}
{renderAdvanceButton()}
{getSlot(slots, 'resetBefore')}
{renderResetButton()}
{getSlot(slots, 'submitBefore')}
{renderSubmitButton()}
{getSlot(slots, 'submitAfter')}
</>
)}
</Form.Item>
)}
</Col>
);
};
},
});
import { Component } from 'vue'; import type { Component } from 'vue';
import type { ComponentType } from './types/index'; import type { ComponentType } from './types/index';
/** /**
...@@ -17,10 +17,11 @@ import { ...@@ -17,10 +17,11 @@ import {
TimePicker, TimePicker,
TreeSelect, TreeSelect,
} from 'ant-design-vue'; } from 'ant-design-vue';
import RadioButtonGroup from './components/RadioButtonGroup.vue'; import RadioButtonGroup from './components/RadioButtonGroup.vue';
import { BasicUpload } from '/@/components/Upload'; import { BasicUpload } from '/@/components/Upload';
const componentMap = new Map<ComponentType, any>(); const componentMap = new Map<ComponentType, Component>();
componentMap.set('Input', Input); componentMap.set('Input', Input);
componentMap.set('InputGroup', Input.Group); componentMap.set('InputGroup', Input.Group);
......
<template>
<a-col
v-bind="actionColOpt"
class="mb-2"
:style="{ textAlign: 'right' }"
v-if="showActionButtonGroup"
>
<FormItem>
<slot name="resetBefore" />
<Button
type="default"
class="mr-2"
v-bind="getResetBtnOptions"
@click="resetAction"
v-if="showResetButton"
>
{{ getResetBtnOptions.text }}
</Button>
<slot name="submitBefore" />
<Button
type="primary"
class="mr-2"
v-bind="getSubmitBtnOptions"
@click="submitAction"
v-if="showSubmitButton"
>
{{ getSubmitBtnOptions.text }}
</Button>
<slot name="advanceBefore" />
<Button
type="link"
size="small"
@click="toggleAdvanced"
v-if="showAdvancedButton && !hideAdvanceBtn"
>
{{ isAdvanced ? t('component.form.putAway') : t('component.form.unfold') }}
<BasicArrow class="ml-1" :expand="!isAdvanced" top />
</Button>
<slot name="advanceAfter" />
</FormItem>
</a-col>
</template>
<script lang="ts">
import type { ColEx } from '../types/index';
import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
import { defineComponent, computed, PropType } from 'vue';
import { Form } from 'ant-design-vue';
import { Button } from '/@/components/Button';
import { BasicArrow } from '/@/components/Basic/index';
import { useFormContext } from '../hooks/useFormContext';
import { useI18n } from '/@/hooks/web/useI18n';
import { propTypes } from '/@/utils/propTypes';
type ButtonOptions = Partial<ButtonProps> & { text: string };
export default defineComponent({
name: 'BasicFormAction',
components: {
FormItem: Form,
Button,
BasicArrow,
},
props: {
showActionButtonGroup: propTypes.bool.def(true),
showResetButton: propTypes.bool.def(true),
showSubmitButton: propTypes.bool.def(true),
showAdvancedButton: propTypes.bool.def(true),
resetButtonOptions: {
type: Object as PropType<ButtonOptions>,
default: {},
},
submitButtonOptions: {
type: Object as PropType<ButtonOptions>,
default: {},
},
actionColOptions: {
type: Object as PropType<Partial<ColEx>>,
default: {},
},
actionSpan: propTypes.number.def(6),
isAdvanced: propTypes.bool,
hideAdvanceBtn: propTypes.bool,
},
setup(props, { emit }) {
const { t } = useI18n();
const actionColOpt = computed(() => {
const { showAdvancedButton, actionSpan: span, actionColOptions } = props;
const actionSpan = 24 - span;
const advancedSpanObj = showAdvancedButton
? { span: actionSpan < 6 ? 24 : actionSpan }
: {};
const actionColOpt: Partial<ColEx> = {
span: showAdvancedButton ? 6 : 4,
...advancedSpanObj,
...actionColOptions,
};
return actionColOpt;
});
const getResetBtnOptions = computed(
(): ButtonOptions => {
return Object.assign(
{
text: t('component.form.resetButton'),
},
props.resetButtonOptions
);
}
);
const getSubmitBtnOptions = computed(() => {
return Object.assign(
{
text: t('component.form.submitButton'),
},
props.submitButtonOptions
);
});
function toggleAdvanced() {
emit('toggle-advanced');
}
return {
t,
actionColOpt,
getResetBtnOptions,
getSubmitBtnOptions,
toggleAdvanced,
...useFormContext(),
};
},
});
</script>
<!--
* @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
-->
<template> <template>
<RadioGroup v-bind="$attrs" v-model:value="valueRef" button-style="solid"> <RadioGroup v-bind="attrs" v-model:value="state" button-style="solid">
<template v-for="item in getOptions" :key="`${item.value}`"> <template v-for="item in getOptions" :key="`${item.value}`">
<RadioButton :value="item.value"> {{ item.label }} </RadioButton> <RadioButton :value="item.value"> {{ item.label }} </RadioButton>
</template> </template>
</RadioGroup> </RadioGroup>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, PropType, watch, unref, computed } from 'vue'; import { defineComponent, PropType, computed } from 'vue';
import { Radio } from 'ant-design-vue'; import { Radio } from 'ant-design-vue';
import {} from 'ant-design-vue/es/radio/Group';
import { isString } from '/@/utils/is'; import { isString } from '/@/utils/is';
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import { useAttrs } from '/@/hooks/core/useAttrs';
type OptionsItem = { label: string; value: string; disabled?: boolean }; type OptionsItem = { label: string; value: string; disabled?: boolean };
type RadioItem = string | OptionsItem; type RadioItem = string | OptionsItem;
export default defineComponent({ export default defineComponent({
name: 'RadioButtonGroup', name: 'RadioButtonGroup',
components: { components: {
...@@ -28,34 +33,22 @@ ...@@ -28,34 +33,22 @@
default: () => [], default: () => [],
}, },
}, },
setup(props, { emit }) { setup(props) {
const valueRef = ref(''); const attrs = useAttrs();
// Embedded in the form, just use the hook binding to perform form verification
watch( const [state] = useRuleFormItem(props);
() => props.value, // Processing options value
(v = '') => {
valueRef.value = v;
},
{ immediate: true }
);
watch(
() => unref(valueRef),
() => {
emit('change', valueRef.value);
},
{ immediate: true }
);
const getOptions = computed((): OptionsItem[] => { const getOptions = computed((): OptionsItem[] => {
const { options } = props; const { options } = props;
if (!options || options.length === 0) return []; if (!options || options?.length === 0) return [];
const isStringArr = options.some((item) => isString(item)); const isStringArr = options.some((item) => isString(item));
if (!isStringArr) return options as OptionsItem[]; if (!isStringArr) return options as OptionsItem[];
return options.map((item) => ({ label: item, value: item })) as OptionsItem[]; return options.map((item) => ({ label: item, value: item })) as OptionsItem[];
}); });
return { valueRef, getOptions }; return { state, getOptions, attrs };
}, },
}); });
</script> </script>
import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
import type { ComponentType } from './types/index'; import type { ComponentType } from './types/index';
import { useI18n } from '/@/hooks/web/useI18n'; import { useI18n } from '/@/hooks/web/useI18n';
...@@ -30,6 +31,16 @@ function genType() { ...@@ -30,6 +31,16 @@ function genType() {
return ['DatePicker', 'MonthPicker', 'RangePicker', 'WeekPicker', 'TimePicker']; return ['DatePicker', 'MonthPicker', 'RangePicker', 'WeekPicker', 'TimePicker'];
} }
export function setComponentRuleType(rule: ValidationRule, component: ComponentType) {
if (['DatePicker', 'MonthPicker', 'WeekPicker', 'TimePicker'].includes(component)) {
rule.type = 'object';
} else if (['RangePicker', 'Upload', 'CheckboxGroup', 'TimePicker'].includes(component)) {
rule.type = 'array';
} else if (['InputNumber'].includes(component)) {
rule.type = 'number';
}
}
/** /**
* 时间字段 * 时间字段
*/ */
......
...@@ -13,28 +13,28 @@ const BASIC_COL_LEN = 24; ...@@ -13,28 +13,28 @@ const BASIC_COL_LEN = 24;
interface UseAdvancedContext { interface UseAdvancedContext {
advanceState: AdvanceState; advanceState: AdvanceState;
emit: EmitType; emit: EmitType;
getMergePropsRef: ComputedRef<FormProps>;
getProps: ComputedRef<FormProps>; getProps: ComputedRef<FormProps>;
getSchema: ComputedRef<FormSchema[]>; getSchema: ComputedRef<FormSchema[]>;
formModel: any; formModel: Recordable;
defaultValueRef: Ref<any>; defaultValueRef: Ref<Recordable>;
} }
export default function ({ export default function ({
advanceState, advanceState,
emit, emit,
getMergePropsRef,
getProps, getProps,
getSchema, getSchema,
formModel, formModel,
defaultValueRef, defaultValueRef,
}: UseAdvancedContext) { }: UseAdvancedContext) {
const { realWidthRef, screenEnum, screenRef } = useBreakpoint(); const { realWidthRef, screenEnum, screenRef } = useBreakpoint();
const getEmptySpanRef = computed((): number => {
const getEmptySpan = computed((): number => {
if (!advanceState.isAdvanced) { if (!advanceState.isAdvanced) {
return 0; return 0;
} }
const emptySpan = unref(getMergePropsRef).emptySpan || 0; // For some special cases, you need to manually specify additional blank lines
const emptySpan = unref(getProps).emptySpan || 0;
if (isNumber(emptySpan)) { if (isNumber(emptySpan)) {
return emptySpan; return emptySpan;
...@@ -49,27 +49,6 @@ export default function ({ ...@@ -49,27 +49,6 @@ export default function ({
return 0; return 0;
}); });
const getActionPropsRef = computed(() => {
const {
resetButtonOptions,
submitButtonOptions,
showActionButtonGroup,
showResetButton,
showSubmitButton,
showAdvancedButton,
actionColOptions,
} = unref(getProps);
return {
resetButtonOptions,
submitButtonOptions,
show: showActionButtonGroup,
showResetButton,
showSubmitButton,
showAdvancedButton,
actionColOptions,
};
});
watch( watch(
[() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)], [() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)],
() => { () => {
...@@ -90,6 +69,7 @@ export default function ({ ...@@ -90,6 +69,7 @@ export default function ({
parseInt(itemCol.sm as string) || parseInt(itemCol.sm as string) ||
(itemCol.span as number) || (itemCol.span as number) ||
BASIC_COL_LEN; BASIC_COL_LEN;
const lgWidth = parseInt(itemCol.lg as string) || mdWidth; const lgWidth = parseInt(itemCol.lg as string) || mdWidth;
const xlWidth = parseInt(itemCol.xl as string) || lgWidth; const xlWidth = parseInt(itemCol.xl as string) || lgWidth;
const xxlWidth = parseInt(itemCol.xxl as string) || xlWidth; const xxlWidth = parseInt(itemCol.xxl as string) || xlWidth;
...@@ -102,15 +82,16 @@ export default function ({ ...@@ -102,15 +82,16 @@ export default function ({
} else { } else {
itemColSum += xxlWidth; itemColSum += xxlWidth;
} }
if (isLastAction) { if (isLastAction) {
advanceState.hideAdvanceBtn = false; advanceState.hideAdvanceBtn = false;
if (itemColSum <= BASIC_COL_LEN * 2) { if (itemColSum <= BASIC_COL_LEN * 2) {
// 小于等于2行时,不显示收起展开按钮 // When less than or equal to 2 lines, the collapse and expand buttons are not displayed
advanceState.hideAdvanceBtn = true; advanceState.hideAdvanceBtn = true;
advanceState.isAdvanced = true; advanceState.isAdvanced = true;
} else if ( } else if (
itemColSum > BASIC_COL_LEN * 2 && itemColSum > BASIC_COL_LEN * 2 &&
itemColSum <= BASIC_COL_LEN * (unref(getMergePropsRef).autoAdvancedLine || 3) itemColSum <= BASIC_COL_LEN * (unref(getProps).autoAdvancedLine || 3)
) { ) {
advanceState.hideAdvanceBtn = false; advanceState.hideAdvanceBtn = false;
...@@ -168,13 +149,9 @@ export default function ({ ...@@ -168,13 +149,9 @@ export default function ({
} }
} }
advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpanRef); advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpan);
getAdvanced( getAdvanced(unref(getProps).actionColOptions || { span: BASIC_COL_LEN }, itemColSum, true);
unref(getActionPropsRef).actionColOptions || { span: BASIC_COL_LEN },
itemColSum,
true
);
emit('advanced-change'); emit('advanced-change');
} }
...@@ -182,5 +159,6 @@ export default function ({ ...@@ -182,5 +159,6 @@ export default function ({
function handleToggleAdvanced() { function handleToggleAdvanced() {
advanceState.isAdvanced = !advanceState.isAdvanced; advanceState.isAdvanced = !advanceState.isAdvanced;
} }
return { getActionPropsRef, handleToggleAdvanced };
return { handleToggleAdvanced };
} }
import type { ComponentType } from '../types/index'; import type { ComponentType } from '../types/index';
import { tryOnUnmounted } from '/@/utils/helper/vueHelper'; import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
import { add, del } from '../componentMap'; import { add, del } from '../componentMap';
export function useComponentRegister(compName: ComponentType, comp: any) { import type { Component } from 'vue';
export function useComponentRegister(compName: ComponentType, comp: Component) {
add(compName, comp); add(compName, comp);
tryOnUnmounted(() => { tryOnUnmounted(() => {
del(compName); del(compName);
......
import { ref, onUnmounted, unref } from 'vue'; import { ref, onUnmounted, unref, nextTick } from 'vue';
import { isInSetup } from '/@/utils/helper/vueHelper'; import { isInSetup } from '/@/utils/helper/vueHelper';
import { isProdMode } from '/@/utils/env'; import { isProdMode } from '/@/utils/env';
import { error } from '/@/utils/log';
import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form'; import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form';
import type { NamePath } from 'ant-design-vue/lib/form/interface'; import type { NamePath } from 'ant-design-vue/lib/form/interface';
export declare type ValidateFields = (nameList?: NamePath[]) => Promise<any>; export declare type ValidateFields = (nameList?: NamePath[]) => Promise<Recordable>;
export function useForm(props?: Partial<FormProps>): UseFormReturnType { export function useForm(props?: Partial<FormProps>): UseFormReturnType {
isInSetup(); isInSetup();
const formRef = ref<FormActionType | null>(null);
const loadedRef = ref<boolean | null>(false);
function getForm() { const formRef = ref<Nullable<FormActionType>>(null);
const loadedRef = ref<Nullable<boolean>>(false);
async function getForm() {
const form = unref(formRef); const form = unref(formRef);
if (!form) { if (!form) {
throw new Error('formRef is Null'); error(
'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!'
);
} }
await nextTick();
return form as FormActionType; return form as FormActionType;
} }
function register(instance: FormActionType) { function register(instance: FormActionType) {
...@@ -27,45 +32,73 @@ export function useForm(props?: Partial<FormProps>): UseFormReturnType { ...@@ -27,45 +32,73 @@ export function useForm(props?: Partial<FormProps>): UseFormReturnType {
loadedRef.value = null; loadedRef.value = null;
}); });
if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return; if (unref(loadedRef) && isProdMode() && instance === unref(formRef)) return;
formRef.value = instance; formRef.value = instance;
props && instance.setProps(props); props && instance.setProps(props);
loadedRef.value = true; loadedRef.value = true;
} }
const methods: FormActionType = { const methods: FormActionType = {
setProps: (formProps: Partial<FormProps>) => { scrollToField: async (name: NamePath, options?: ScrollOptions | undefined) => {
getForm().setProps(formProps); const form = await getForm();
form.scrollToField(name, options);
}, },
updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => { setProps: async (formProps: Partial<FormProps>) => {
getForm().updateSchema(data); const form = await getForm();
form.setProps(formProps);
}, },
clearValidate: (name?: string | string[]) => {
getForm().clearValidate(name); updateSchema: async (data: Partial<FormSchema> | Partial<FormSchema>[]) => {
const form = await getForm();
form.updateSchema(data);
},
clearValidate: async (name?: string | string[]) => {
const form = await getForm();
form.clearValidate(name);
}, },
resetFields: async () => { resetFields: async () => {
await getForm().resetFields(); getForm().then(async (form) => {
await form.resetFields();
});
}, },
removeSchemaByFiled: (field: string | string[]) => {
getForm().removeSchemaByFiled(field); removeSchemaByFiled: async (field: string | string[]) => {
const form = await getForm();
form.removeSchemaByFiled(field);
}, },
getFieldsValue: () => {
return getForm().getFieldsValue(); // TODO promisify
getFieldsValue: <T>() => {
return unref(formRef)?.getFieldsValue() as T;
}, },
setFieldsValue: <T>(values: T) => {
getForm().setFieldsValue<T>(values); setFieldsValue: async <T>(values: T) => {
const form = await getForm();
form.setFieldsValue<T>(values);
}, },
appendSchemaByField: (schema: FormSchema, prefixField?: string | undefined) => {
getForm().appendSchemaByField(schema, prefixField); appendSchemaByField: async (schema: FormSchema, prefixField?: string | undefined) => {
const form = await getForm();
form.appendSchemaByField(schema, prefixField);
}, },
submit: async (): Promise<any> => { submit: async (): Promise<any> => {
return getForm().submit(); const form = await getForm();
return form.submit();
},
validate: async (nameList?: NamePath[]): Promise<Recordable> => {
const form = await getForm();
return form.validate(nameList);
},
validateFields: async (nameList?: NamePath[]): Promise<Recordable> => {
const form = await getForm();
return form.validateFields(nameList);
}, },
validate: ((async (nameList?: NamePath[]): Promise<any> => { };
return getForm().validate(nameList);
}) as any) as ValidateFields,
validateFields: ((async (nameList?: NamePath[]): Promise<any> => {
return getForm().validate(nameList);
}) as any) as ValidateFields,
} as FormActionType;
return [register, methods]; return [register, methods];
} }
import { InjectionKey } from 'vue';
import { createContext, useContext } from '/@/hooks/core/useContext';
export interface FormContextProps {
resetAction: () => Promise<void>;
submitAction: () => Promise<void>;
}
const key: InjectionKey<FormContextProps> = Symbol();
export function createFormContext(context: FormContextProps) {
return createContext<FormContextProps>(context, key);
}
export function useFormContext() {
return useContext<FormContextProps>(key);
}
...@@ -9,22 +9,19 @@ import { deepMerge, unique } from '/@/utils'; ...@@ -9,22 +9,19 @@ import { deepMerge, unique } from '/@/utils';
import { dateItemType } from '../helper'; import { dateItemType } from '../helper';
import moment from 'moment'; import moment from 'moment';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { error } from '/@/utils/log';
interface UseFormActionContext { interface UseFormActionContext {
emit: EmitType; emit: EmitType;
getProps: ComputedRef<FormProps>; getProps: ComputedRef<FormProps>;
getSchema: ComputedRef<FormSchema[]>; getSchema: ComputedRef<FormSchema[]>;
formModel: any; formModel: Recordable;
defaultValueRef: Ref<any>; defaultValueRef: Ref<Recordable>;
formElRef: Ref<FormActionType>; formElRef: Ref<FormActionType>;
schemaRef: Ref<FormSchema[]>; schemaRef: Ref<FormSchema[]>;
handleFormValues: Fn; handleFormValues: Fn;
actionState: {
resetAction: any;
submitAction: any;
};
} }
export function useFormAction({ export function useFormEvents({
emit, emit,
getProps, getProps,
formModel, formModel,
...@@ -33,34 +30,34 @@ export function useFormAction({ ...@@ -33,34 +30,34 @@ export function useFormAction({
formElRef, formElRef,
schemaRef, schemaRef,
handleFormValues, handleFormValues,
actionState,
}: UseFormActionContext) { }: UseFormActionContext) {
async function resetFields(): Promise<any> { async function resetFields(): Promise<void> {
const { resetFunc, submitOnReset } = unref(getProps); const { resetFunc, submitOnReset } = unref(getProps);
resetFunc && isFunction(resetFunc) && (await resetFunc()); resetFunc && isFunction(resetFunc) && (await resetFunc());
const formEl = unref(formElRef); const formEl = unref(formElRef);
if (!formEl) return; if (!formEl) return;
Object.keys(formModel).forEach((key) => { Object.keys(formModel).forEach((key) => {
(formModel as any)[key] = defaultValueRef.value[key]; formModel[key] = defaultValueRef.value[key];
}); });
clearValidate(); clearValidate();
emit('reset', toRaw(formModel)); emit('reset', toRaw(formModel));
// return values;
submitOnReset && handleSubmit(); submitOnReset && handleSubmit();
} }
/** /**
* @description: 设置表单值 * @description: Set form value
*/ */
async function setFieldsValue(values: any): Promise<void> { async function setFieldsValue(values: any): Promise<void> {
const fields = unref(getSchema) const fields = unref(getSchema)
.map((item) => item.field) .map((item) => item.field)
.filter(Boolean); .filter(Boolean);
// const formEl = unref(formElRef);
const validKeys: string[] = []; const validKeys: string[] = [];
Object.keys(values).forEach((key) => { Object.keys(values).forEach((key) => {
const element = values[key]; const element = values[key];
// 0| '' is allow
if (element !== undefined && element !== null && fields.includes(key)) { if (element !== undefined && element !== null && fields.includes(key)) {
// time type // time type
if (itemIsDateType(key)) { if (itemIsDateType(key)) {
...@@ -69,12 +66,12 @@ export function useFormAction({ ...@@ -69,12 +66,12 @@ export function useFormAction({
for (const ele of element) { for (const ele of element) {
arr.push(moment(ele)); arr.push(moment(ele));
} }
(formModel as any)[key] = arr; formModel[key] = arr;
} else { } else {
(formModel as any)[key] = moment(element); formModel[key] = moment(element);
} }
} else { } else {
(formModel as any)[key] = element; formModel[key] = element;
} }
validKeys.push(key); validKeys.push(key);
} }
...@@ -84,19 +81,18 @@ export function useFormAction({ ...@@ -84,19 +81,18 @@ export function useFormAction({
/** /**
* @description: Delete based on field name * @description: Delete based on field name
*/ */
function removeSchemaByFiled(fields: string | string[]): void { async function removeSchemaByFiled(fields: string | string[]): Promise<void> {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
if (!fields) { if (!fields) return;
return;
} let fieldList: string[] = isString(fields) ? [fields] : fields;
let fieldList: string[] = fields as string[];
if (isString(fields)) { if (isString(fields)) {
fieldList = [fields]; fieldList = [fields];
} }
for (const field of fieldList) { for (const field of fieldList) {
_removeSchemaByFiled(field, schemaList); _removeSchemaByFiled(field, schemaList);
} }
schemaRef.value = schemaList as any; schemaRef.value = schemaList;
} }
/** /**
...@@ -114,27 +110,26 @@ export function useFormAction({ ...@@ -114,27 +110,26 @@ export function useFormAction({
/** /**
* @description: Insert after a certain field, if not insert the last * @description: Insert after a certain field, if not insert the last
*/ */
function appendSchemaByField(schema: FormSchema, prefixField?: string) { async function appendSchemaByField(schema: FormSchema, prefixField?: string, first = false) {
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
const index = schemaList.findIndex((schema) => schema.field === prefixField); const index = schemaList.findIndex((schema) => schema.field === prefixField);
const hasInList = schemaList.find((item) => item.field === schema.field); const hasInList = schemaList.some((item) => item.field === schema.field);
if (hasInList) { if (!hasInList) return;
return;
} if (!prefixField || index === -1 || first) {
if (!prefixField || index === -1) { first ? schemaList.unshift(schema) : schemaList.push(schema);
schemaList.push(schema); schemaRef.value = schemaList;
schemaRef.value = schemaList as any;
return; return;
} }
if (index !== -1) { if (index !== -1) {
schemaList.splice(index + 1, 0, schema); schemaList.splice(index + 1, 0, schema);
} }
schemaRef.value = schemaList as any; schemaRef.value = schemaList;
} }
function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) { async function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {
let updateData: Partial<FormSchema>[] = []; let updateData: Partial<FormSchema>[] = [];
if (isObject(data)) { if (isObject(data)) {
updateData.push(data as FormSchema); updateData.push(data as FormSchema);
...@@ -142,9 +137,13 @@ export function useFormAction({ ...@@ -142,9 +137,13 @@ export function useFormAction({
if (isArray(data)) { if (isArray(data)) {
updateData = [...data]; updateData = [...data];
} }
const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field); const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field);
if (!hasField) { if (!hasField) {
throw new Error('Must pass in the `field` field!'); error(
'All children of the form Schema array that need to be updated must contain the `field` field'
);
} }
const schema: FormSchema[] = []; const schema: FormSchema[] = [];
updateData.forEach((item) => { updateData.forEach((item) => {
...@@ -157,12 +156,12 @@ export function useFormAction({ ...@@ -157,12 +156,12 @@ export function useFormAction({
} }
}); });
}); });
schemaRef.value = unique(schema, 'field') as any; schemaRef.value = unique(schema, 'field');
} }
function getFieldsValue(): any { function getFieldsValue(): Recordable {
const formEl = unref(formElRef); const formEl = unref(formElRef);
if (!formEl) return; if (!formEl) return {};
return handleFormValues(toRaw(unref(formModel))); return handleFormValues(toRaw(unref(formModel)));
} }
...@@ -171,23 +170,24 @@ export function useFormAction({ ...@@ -171,23 +170,24 @@ export function useFormAction({
*/ */
function itemIsDateType(key: string) { function itemIsDateType(key: string) {
return unref(getSchema).some((item) => { return unref(getSchema).some((item) => {
return item.field === key ? dateItemType.includes(item.component!) : false; return item.field === key ? dateItemType.includes(item.component) : false;
}); });
} }
function validateFields(nameList?: NamePath[] | undefined) { async function validateFields(nameList?: NamePath[] | undefined) {
if (!formElRef.value) return; return await unref(formElRef)?.validateFields(nameList);
return formElRef.value.validateFields(nameList); }
async function validate(nameList?: NamePath[] | undefined) {
return await unref(formElRef)?.validate(nameList);
} }
function validate(nameList?: NamePath[] | undefined) { async function clearValidate(name?: string | string[]) {
if (!formElRef.value) return; await unref(formElRef)?.clearValidate(name);
return formElRef.value.validate(nameList);
} }
function clearValidate(name?: string | string[]) { async function scrollToField(name: NamePath, options?: ScrollOptions | undefined) {
if (!formElRef.value) return; await unref(formElRef)?.scrollToField(name, options);
formElRef.value.clearValidate(name);
} }
/** /**
...@@ -208,13 +208,6 @@ export function useFormAction({ ...@@ -208,13 +208,6 @@ export function useFormAction({
emit('submit', res); emit('submit', res);
} catch (error) {} } catch (error) {}
} }
actionState.resetAction = {
onClick: resetFields,
};
actionState.submitAction = {
onClick: handleSubmit,
};
return { return {
handleSubmit, handleSubmit,
...@@ -227,5 +220,6 @@ export function useFormAction({ ...@@ -227,5 +220,6 @@ export function useFormAction({
removeSchemaByFiled, removeSchemaByFiled,
resetFields, resetFields,
setFieldsValue, setFieldsValue,
scrollToField,
}; };
} }
...@@ -9,7 +9,7 @@ interface UseFormValuesContext { ...@@ -9,7 +9,7 @@ interface UseFormValuesContext {
fieldMapToTimeRef: Ref<FieldMapToTime>; fieldMapToTimeRef: Ref<FieldMapToTime>;
defaultValueRef: Ref<any>; defaultValueRef: Ref<any>;
getSchema: ComputedRef<FormSchema[]>; getSchema: ComputedRef<FormSchema[]>;
formModel: any; formModel: Recordable;
} }
export function useFormValues({ export function useFormValues({
transformDateFuncRef, transformDateFuncRef,
...@@ -19,11 +19,11 @@ export function useFormValues({ ...@@ -19,11 +19,11 @@ export function useFormValues({
formModel, formModel,
}: UseFormValuesContext) { }: UseFormValuesContext) {
// Processing form values // Processing form values
function handleFormValues(values: Record<string, any>) { function handleFormValues(values: Recordable) {
if (!isObject(values)) { if (!isObject(values)) {
return {}; return {};
} }
const resMap: Record<string, any> = {}; const res: Recordable = {};
for (const item of Object.entries(values)) { for (const item of Object.entries(values)) {
let [, value] = item; let [, value] = item;
const [key] = item; const [key] = item;
...@@ -41,15 +41,15 @@ export function useFormValues({ ...@@ -41,15 +41,15 @@ export function useFormValues({
if (isString(value)) { if (isString(value)) {
value = value.trim(); value = value.trim();
} }
resMap[key] = value; res[key] = value;
} }
return handleRangeTimeValue(resMap); return handleRangeTimeValue(res);
} }
/** /**
* @description: Processing time interval parameters * @description: Processing time interval parameters
*/ */
function handleRangeTimeValue(values: Record<string, any>) { function handleRangeTimeValue(values: Recordable) {
const fieldMapToTime = unref(fieldMapToTimeRef); const fieldMapToTime = unref(fieldMapToTimeRef);
if (!fieldMapToTime || !Array.isArray(fieldMapToTime)) { if (!fieldMapToTime || !Array.isArray(fieldMapToTime)) {
...@@ -65,6 +65,7 @@ export function useFormValues({ ...@@ -65,6 +65,7 @@ export function useFormValues({
values[startTimeKey] = moment(startTime).format(format); values[startTimeKey] = moment(startTime).format(format);
values[endTimeKey] = moment(endTime).format(format); values[endTimeKey] = moment(endTime).format(format);
Reflect.deleteProperty(values, field);
} }
return values; return values;
...@@ -72,11 +73,11 @@ export function useFormValues({ ...@@ -72,11 +73,11 @@ export function useFormValues({
function initDefault() { function initDefault() {
const schemas = unref(getSchema); const schemas = unref(getSchema);
const obj: Record<string, any> = {}; const obj: Recordable = {};
schemas.forEach((item) => { schemas.forEach((item) => {
if (item.defaultValue) { if (item.defaultValue) {
obj[item.field] = item.defaultValue; obj[item.field] = item.defaultValue;
(formModel as any)[item.field] = item.defaultValue; formModel[item.field] = item.defaultValue;
} }
}); });
defaultValueRef.value = obj; defaultValueRef.value = obj;
......
...@@ -4,23 +4,8 @@ import type { FormProps, FormSchema } from '../types/form'; ...@@ -4,23 +4,8 @@ import type { FormProps, FormSchema } from '../types/form';
import { computed, unref } from 'vue'; import { computed, unref } from 'vue';
import { isNumber } from '/@/utils/is'; import { isNumber } from '/@/utils/is';
// export function useGlobalLabelWidth(propsRef: ComputedRef<FormProps>) {
// return computed(() => {
// const { labelWidth, labelCol, wrapperCol } = unref(propsRef);
// if (!labelWidth) {
// return { labelCol, wrapperCol };
// }
// const width = isNumber(labelWidth) ? `${labelWidth}px` : labelWidth;
// return {
// labelCol: { style: { width }, span: 1, ...labelCol },
// wrapperCol: { style: { width: `calc(100% - ${width})` }, span: 23, ...wrapperCol },
// };
// });
// }
export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<FormProps>) { export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<FormProps>) {
return computed((): any => { return computed(() => {
const schemaItem = unref(schemaItemRef); const schemaItem = unref(schemaItemRef);
const { labelCol = {}, wrapperCol = {} } = schemaItem.itemProps || {}; const { labelCol = {}, wrapperCol = {} } = schemaItem.itemProps || {};
const { labelWidth, disabledLabelWidth } = schemaItem; const { labelWidth, disabledLabelWidth } = schemaItem;
...@@ -29,7 +14,7 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref< ...@@ -29,7 +14,7 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
labelWidth: globalLabelWidth, labelWidth: globalLabelWidth,
labelCol: globalLabelCol, labelCol: globalLabelCol,
wrapperCol: globWrapperCol, wrapperCol: globWrapperCol,
} = unref(propsRef) as any; } = unref(propsRef);
// If labelWidth is set globally, all items setting // If labelWidth is set globally, all items setting
if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) { if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) {
......
import type { FieldMapToTime, FormSchema } from './types/form'; import type { FieldMapToTime, FormSchema } from './types/form';
import type { PropType } from 'vue'; import type { CSSProperties, PropType } from 'vue';
import type { ColEx } from './types'; import type { ColEx } from './types';
import { TableActionType } from '/@/components/Table'; import type { TableActionType } from '/@/components/Table';
import type { ButtonProps } from 'ant-design-vue/es/button/buttonTypes';
import { propTypes } from '/@/utils/propTypes';
export const basicProps = { export const basicProps = {
model: { model: {
type: Object as PropType<Record<string, any>>, type: Object as PropType<Recordable>,
default: {}, default: {},
}, },
// 标签宽度 固定宽度 // 标签宽度 固定宽度
...@@ -17,7 +20,7 @@ export const basicProps = { ...@@ -17,7 +20,7 @@ export const basicProps = {
type: Array as PropType<FieldMapToTime>, type: Array as PropType<FieldMapToTime>,
default: () => [], default: () => [],
}, },
compact: Boolean as PropType<boolean>, compact: propTypes.bool,
// 表单配置规则 // 表单配置规则
schemas: { schemas: {
type: [Array] as PropType<FormSchema[]>, type: [Array] as PropType<FormSchema[]>,
...@@ -25,98 +28,68 @@ export const basicProps = { ...@@ -25,98 +28,68 @@ export const basicProps = {
required: true, required: true,
}, },
mergeDynamicData: { mergeDynamicData: {
type: Object as PropType<any>, type: Object as PropType<Recordable>,
default: null, default: null,
}, },
baseRowStyle: { baseRowStyle: {
type: Object as PropType<any>, type: Object as PropType<CSSProperties>,
}, },
baseColProps: { baseColProps: {
type: Object as PropType<any>, type: Object as PropType<Partial<ColEx>>,
},
autoSetPlaceHolder: {
type: Boolean,
default: true,
},
submitOnReset: {
type: Boolean,
default: false,
},
size: {
type: String as PropType<'default' | 'small' | 'large'>,
default: 'default',
}, },
autoSetPlaceHolder: propTypes.bool.def(true),
submitOnReset: propTypes.bool,
size: propTypes.oneOf(['default', 'small', 'large']).def('default'),
// 禁用表单 // 禁用表单
disabled: Boolean as PropType<boolean>, disabled: propTypes.bool,
emptySpan: { emptySpan: {
type: [Number, Object] as PropType<number>, type: [Number, Object] as PropType<number>,
default: 0, default: 0,
}, },
// 是否显示收起展开按钮 // 是否显示收起展开按钮
showAdvancedButton: { type: Boolean as PropType<boolean>, default: false }, showAdvancedButton: propTypes.bool,
// 转化时间 // 转化时间
transformDateFunc: { transformDateFunc: {
type: Function as PropType<Fn>, type: Function as PropType<Fn>,
default: (date: any) => { default: (date: any) => {
return date._isAMomentObject ? date.format('YYYY-MM-DD HH:mm:ss') : date; return date._isAMomentObject ? date?.format('YYYY-MM-DD HH:mm:ss') : date;
},
}, },
rulesMessageJoinLabel: {
type: Boolean,
default: true,
}, },
rulesMessageJoinLabel: propTypes.bool.def(true),
// 超过3行自动折叠 // 超过3行自动折叠
autoAdvancedLine: { autoAdvancedLine: propTypes.number.def(3),
type: Number as PropType<number>,
default: 3,
},
// 是否显示操作按钮 // 是否显示操作按钮
showActionButtonGroup: { showActionButtonGroup: propTypes.bool.def(true),
type: Boolean as PropType<boolean>,
default: true,
},
// 操作列Col配置 // 操作列Col配置
actionColOptions: Object as PropType<ColEx>, actionColOptions: Object as PropType<Partial<ColEx>>,
// 显示重置按钮 // 显示重置按钮
showResetButton: { showResetButton: propTypes.bool.def(true),
type: Boolean as PropType<boolean>,
default: true,
},
// 重置按钮配置 // 重置按钮配置
resetButtonOptions: Object as PropType<any>, resetButtonOptions: Object as PropType<Partial<ButtonProps>>,
// 显示确认按钮 // 显示确认按钮
showSubmitButton: { showSubmitButton: propTypes.bool.def(true),
type: Boolean as PropType<boolean>,
default: true,
},
// 确认按钮配置 // 确认按钮配置
submitButtonOptions: Object as PropType<any>, submitButtonOptions: Object as PropType<Partial<ButtonProps>>,
// 自定义重置函数 // 自定义重置函数
resetFunc: Function as PropType<Fn>, resetFunc: Function as PropType<() => Promise<void>>,
submitFunc: Function as PropType<Fn>, submitFunc: Function as PropType<() => Promise<void>>,
// 以下为默认props // 以下为默认props
hideRequiredMark: Boolean as PropType<boolean>, hideRequiredMark: propTypes.bool,
labelCol: Object as PropType<ColEx>, labelCol: Object as PropType<Partial<ColEx>>,
layout: { layout: propTypes.oneOf(['horizontal', 'vertical', 'inline']).def('horizontal'),
type: String as PropType<'horizontal' | 'vertical' | 'inline'>,
default: 'horizontal',
},
tableAction: { tableAction: {
type: Object as PropType<TableActionType>, type: Object as PropType<TableActionType>,
}, },
wrapperCol: Object as PropType<any>, wrapperCol: Object as PropType<Partial<ColEx>>,
colon: { colon: propTypes.bool,
type: Boolean as PropType<boolean>,
default: false,
},
labelAlign: String as PropType<string>, labelAlign: propTypes.string,
}; };
...@@ -5,6 +5,7 @@ import type { ButtonProps as AntdButtonProps } from 'ant-design-vue/es/button/bu ...@@ -5,6 +5,7 @@ import type { ButtonProps as AntdButtonProps } from 'ant-design-vue/es/button/bu
import type { FormItem } from './formItem'; import type { FormItem } from './formItem';
import type { ColEx, ComponentType } from './index'; import type { ColEx, ComponentType } from './index';
import type { TableActionType } from '/@/components/Table/src/types/table'; import type { TableActionType } from '/@/components/Table/src/types/table';
import type { CSSProperties } from 'vue';
export type FieldMapToTime = [string, [string, string], string?][]; export type FieldMapToTime = [string, [string, string], string?][];
...@@ -14,8 +15,8 @@ export type Rule = RuleObject & { ...@@ -14,8 +15,8 @@ export type Rule = RuleObject & {
export interface RenderCallbackParams { export interface RenderCallbackParams {
schema: FormSchema; schema: FormSchema;
values: any; values: Recordable;
model: any; model: Recordable;
field: string; field: string;
} }
...@@ -25,18 +26,19 @@ export interface ButtonProps extends AntdButtonProps { ...@@ -25,18 +26,19 @@ export interface ButtonProps extends AntdButtonProps {
export interface FormActionType { export interface FormActionType {
submit: () => Promise<void>; submit: () => Promise<void>;
setFieldsValue: <T>(values: T) => void; setFieldsValue: <T>(values: T) => Promise<void>;
resetFields: () => Promise<any>; resetFields: () => Promise<void>;
getFieldsValue: () => any; getFieldsValue: () => Recordable;
clearValidate: (name?: string | string[]) => void; clearValidate: (name?: string | string[]) => Promise<void>;
updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => void; updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
setProps: (formProps: Partial<FormProps>) => void; setProps: (formProps: Partial<FormProps>) => Promise<void>;
removeSchemaByFiled: (field: string | string[]) => void; removeSchemaByFiled: (field: string | string[]) => Promise<void>;
appendSchemaByField: (schema: FormSchema, prefixField?: string) => void; appendSchemaByField: (schema: FormSchema, prefixField?: string) => Promise<void>;
validateFields: (nameList?: NamePath[]) => Promise<any>; validateFields: (nameList?: NamePath[]) => Promise<any>;
validate: (nameList?: NamePath[]) => Promise<any>; validate: (nameList?: NamePath[]) => Promise<any>;
scrollToField: (name: NamePath, options?: ScrollOptions) => void; scrollToField: (name: NamePath, options?: ScrollOptions) => Promise<void>;
} }
export type RegisterFn = (formInstance: FormActionType) => void; export type RegisterFn = (formInstance: FormActionType) => void;
export type UseFormReturnType = [RegisterFn, FormActionType]; export type UseFormReturnType = [RegisterFn, FormActionType];
...@@ -44,7 +46,7 @@ export type UseFormReturnType = [RegisterFn, FormActionType]; ...@@ -44,7 +46,7 @@ export type UseFormReturnType = [RegisterFn, FormActionType];
export interface FormProps { export interface FormProps {
// layout?: 'vertical' | 'inline' | 'horizontal'; // layout?: 'vertical' | 'inline' | 'horizontal';
// Form value // Form value
model?: any; model?: Recordable;
// The width of all items in the entire form // The width of all items in the entire form
labelWidth?: number | string; labelWidth?: number | string;
// Submit form on reset // Submit form on reset
...@@ -55,7 +57,7 @@ export interface FormProps { ...@@ -55,7 +57,7 @@ export interface FormProps {
wrapperCol?: Partial<ColEx>; wrapperCol?: Partial<ColEx>;
// General row style // General row style
baseRowStyle?: object; baseRowStyle?: CSSProperties;
// General col configuration // General col configuration
baseColProps?: Partial<ColEx>; baseColProps?: Partial<ColEx>;
...@@ -63,7 +65,7 @@ export interface FormProps { ...@@ -63,7 +65,7 @@ export interface FormProps {
// Form configuration rules // Form configuration rules
schemas?: FormSchema[]; schemas?: FormSchema[];
// Function values used to merge into dynamic control form items // Function values used to merge into dynamic control form items
mergeDynamicData?: any; mergeDynamicData?: Recordable;
// Compact mode for search forms // Compact mode for search forms
compact?: boolean; compact?: boolean;
// Blank line span // Blank line span
...@@ -131,8 +133,8 @@ export interface FormSchema { ...@@ -131,8 +133,8 @@ export interface FormSchema {
schema: FormSchema; schema: FormSchema;
tableAction: TableActionType; tableAction: TableActionType;
formActionType: FormActionType; formActionType: FormActionType;
formModel: any; formModel: Recordable;
}) => any) }) => Recordable)
| object; | object;
// Required // Required
required?: boolean; required?: boolean;
......
...@@ -5,12 +5,12 @@ export interface ModalContextProps { ...@@ -5,12 +5,12 @@ export interface ModalContextProps {
redoModalHeight: () => void; redoModalHeight: () => void;
} }
const modalContextInjectKey: InjectionKey<ModalContextProps> = Symbol(); const key: InjectionKey<ModalContextProps> = Symbol();
export function createModalContext(context: ModalContextProps) { export function createModalContext(context: ModalContextProps) {
return createContext<ModalContextProps>(context, modalContextInjectKey); return createContext<ModalContextProps>(context, key);
} }
export function useModalContext() { export function useModalContext() {
return useContext<ModalContextProps>(modalContextInjectKey); return useContext<ModalContextProps>(key);
} }
...@@ -39,7 +39,7 @@ function extend<T, K>(to: T, _from: K): T & K { ...@@ -39,7 +39,7 @@ function extend<T, K>(to: T, _from: K): T & K {
return Object.assign(to, _from); return Object.assign(to, _from);
} }
export function toObject<T>(arr: Array<T>): Record<string, T> { export function toObject<T>(arr: Array<T>): Recordable<T> {
const res = {}; const res = {};
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
if (arr[i]) { if (arr[i]) {
......
...@@ -221,7 +221,7 @@ ...@@ -221,7 +221,7 @@
function handleTableChange( function handleTableChange(
pagination: PaginationProps, pagination: PaginationProps,
// @ts-ignore // @ts-ignore
filters: Partial<Record<string, string[]>>, filters: Partial<Recordable<string[]>>,
sorter: SorterResult sorter: SorterResult
) { ) {
const { clearSelectOnPageChange, sortFn } = unref(getMergeProps); const { clearSelectOnPageChange, sortFn } = unref(getMergeProps);
......
...@@ -232,7 +232,7 @@ export function renderEditableRow({ ...@@ -232,7 +232,7 @@ export function renderEditableRow({
}; };
} }
export type EditRecordRow<T = { [key: string]: any }> = { export type EditRecordRow<T = Hash<any>> = {
editable: boolean; editable: boolean;
onCancel: Fn; onCancel: Fn;
onSubmit: Fn; onSubmit: Fn;
......
...@@ -194,5 +194,5 @@ export interface ColumnProps<T> { ...@@ -194,5 +194,5 @@ export interface ColumnProps<T> {
* such as slots: { filterIcon: 'XXX'} * such as slots: { filterIcon: 'XXX'}
* @type object * @type object
*/ */
slots?: Record<string, string>; slots?: Recordable<string>;
} }
...@@ -40,7 +40,7 @@ export function createSimpleTransition(name: string, origin = 'top center 0', mo ...@@ -40,7 +40,7 @@ export function createSimpleTransition(name: string, origin = 'top center 0', mo
} }
export function createJavascriptTransition( export function createJavascriptTransition(
name: string, name: string,
functions: Record<string, any>, functions: Recordable,
mode: Mode = 'in-out' mode: Mode = 'in-out'
) { ) {
return defineComponent({ return defineComponent({
......
...@@ -54,7 +54,7 @@ export default defineComponent({ ...@@ -54,7 +54,7 @@ export default defineComponent({
const getWrapStyleRef = computed( const getWrapStyleRef = computed(
(): CSSProperties => { (): CSSProperties => {
const styles: Record<string, string> = {}; const styles: Recordable<string> = {};
const height = convertToUnit(props.height); const height = convertToUnit(props.height);
const minHeight = convertToUnit(props.minHeight); const minHeight = convertToUnit(props.minHeight);
const minWidth = convertToUnit(props.minWidth); const minWidth = convertToUnit(props.minWidth);
......
...@@ -40,7 +40,7 @@ const pattern = { ...@@ -40,7 +40,7 @@ const pattern = {
} as const; } as const;
function parseStyle(style: string) { function parseStyle(style: string) {
const styleMap: Dictionary<any> = {}; const styleMap: Recordable = {};
for (const s of style.split(pattern.styleList)) { for (const s of style.split(pattern.styleList)) {
let [key, val] = s.split(pattern.styleProp); let [key, val] = s.split(pattern.styleProp);
...@@ -161,8 +161,8 @@ export function mergeClasses(target: any, source: any) { ...@@ -161,8 +161,8 @@ export function mergeClasses(target: any, source: any) {
} }
export function mergeListeners( export function mergeListeners(
target: { [key: string]: Function | Function[] } | undefined, target: Indexable<Function | Function[]> | undefined,
source: { [key: string]: Function | Function[] } | undefined source: Indexable<Function | Function[]> | undefined
) { ) {
if (!target) return source; if (!target) return source;
if (!source) return target; if (!source) return target;
......
...@@ -154,7 +154,7 @@ function rippler({ ...@@ -154,7 +154,7 @@ function rippler({
setTimeout(() => { setTimeout(() => {
let clearPosition = true; let clearPosition = true;
for (let i = 0; i < el.childNodes.length; i++) { for (let i = 0; i < el.childNodes.length; i++) {
if ((el.childNodes[i] as any).className === 'ripple-container') { if ((el.childNodes[i] as Recordable).className === 'ripple-container') {
clearPosition = false; clearPosition = false;
} }
} }
...@@ -173,7 +173,7 @@ function rippler({ ...@@ -173,7 +173,7 @@ function rippler({
clearRipple(); clearRipple();
} }
(el as any).setBackground = (bgColor: string) => { (el as Recordable).setBackground = (bgColor: string) => {
if (!bgColor) { if (!bgColor) {
return; return;
} }
...@@ -181,8 +181,8 @@ function rippler({ ...@@ -181,8 +181,8 @@ function rippler({
}; };
} }
function setProps(modifiers: { [key: string]: any }, props: Record<string, any>) { function setProps(modifiers: Hash<any>, props: Recordable) {
modifiers.forEach((item: any) => { modifiers.forEach((item: Recordable) => {
if (isNaN(Number(item))) props.event = item; if (isNaN(Number(item))) props.event = item;
else props.transition = item; else props.transition = item;
}); });
......
import type { UnwrapRef } from 'vue';
import { reactive, readonly, computed, getCurrentInstance } from 'vue';
import { isEqual } from 'lodash-es';
export function useRuleFormItem<T extends Indexable>(
props: T,
key: keyof T = 'value',
changeEvent = 'change'
) {
const instance = getCurrentInstance();
const emit = instance?.emit;
const innerState = reactive({
value: props[key],
});
const defaultState = readonly(innerState);
const setState = (val: UnwrapRef<T[keyof T]>) => {
innerState.value = val as T[keyof T];
};
const state = computed({
get() {
return innerState.value;
},
set(value) {
if (isEqual(value, defaultState.value)) return;
innerState.value = value as T[keyof T];
emit?.(changeEvent, value);
},
});
return [state, setState, defaultState];
}
import { getCurrentInstance, reactive, shallowRef, watchEffect } from 'vue';
interface Params {
excludeListeners?: boolean;
excludeKeys?: string[];
}
const DEFAULT_EXCLUDE_KEYS = ['class', 'style'];
const LISTENER_PREFIX = /^on[A-Z]/;
export function entries<T>(obj: Hash<T>): [string, T][] {
return Object.keys(obj).map((key: string) => [key, obj[key]]);
}
export function useAttrs(params: Params = {}) {
const instance = getCurrentInstance();
if (!instance) return {};
const { excludeListeners = false, excludeKeys = [] } = params;
const attrs = shallowRef({});
const allExcludeKeys = excludeKeys.concat(DEFAULT_EXCLUDE_KEYS);
// Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance
instance.attrs = reactive(instance.attrs);
watchEffect(() => {
const res = entries(instance.attrs).reduce((acm, [key, val]) => {
if (!allExcludeKeys.includes(key) && !(excludeListeners && LISTENER_PREFIX.test(key))) {
acm[key] = val;
}
return acm;
}, {} as Hash<any>);
attrs.value = res;
});
return attrs;
}
...@@ -23,7 +23,7 @@ export type EventOption = { ...@@ -23,7 +23,7 @@ export type EventOption = {
const defaultEvents: keyEvent[] = ['keydown']; const defaultEvents: keyEvent[] = ['keydown'];
// 键盘事件 keyCode 别名 // 键盘事件 keyCode 别名
const aliasKeyCodeMap: Record<string, number | number[]> = { const aliasKeyCodeMap: Recordable<number | number[]> = {
esc: 27, esc: 27,
tab: 9, tab: 9,
enter: 13, enter: 13,
...@@ -36,7 +36,7 @@ const aliasKeyCodeMap: Record<string, number | number[]> = { ...@@ -36,7 +36,7 @@ const aliasKeyCodeMap: Record<string, number | number[]> = {
}; };
// 键盘事件 key 别名 // 键盘事件 key 别名
const aliasKeyMap: Record<string, string | string[]> = { const aliasKeyMap: Recordable<string | string[]> = {
esc: 'Escape', esc: 'Escape',
tab: 'Tab', tab: 'Tab',
enter: 'Enter', enter: 'Enter',
...@@ -50,7 +50,7 @@ const aliasKeyMap: Record<string, string | string[]> = { ...@@ -50,7 +50,7 @@ const aliasKeyMap: Record<string, string | string[]> = {
}; };
// 修饰键 // 修饰键
const modifierKey: Record<string, (event: KeyboardEvent) => boolean> = { const modifierKey: Recordable<(event: KeyboardEvent) => boolean> = {
ctrl: (event: KeyboardEvent) => event.ctrlKey, ctrl: (event: KeyboardEvent) => event.ctrlKey,
shift: (event: KeyboardEvent) => event.shiftKey, shift: (event: KeyboardEvent) => event.shiftKey,
alt: (event: KeyboardEvent) => event.altKey, alt: (event: KeyboardEvent) => event.altKey,
......
...@@ -24,7 +24,7 @@ export function useI18n(namespace?: string) { ...@@ -24,7 +24,7 @@ export function useI18n(namespace?: string) {
return { return {
...methods, ...methods,
t: (key: string, ...arg: any) => { t: (key: string, ...arg: any): string => {
if (!key) return ''; if (!key) return '';
return t(getKey(key), ...(arg as Parameters<typeof t>)); return t(getKey(key), ...(arg as Parameters<typeof t>));
}, },
......
...@@ -13,7 +13,7 @@ import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; ...@@ -13,7 +13,7 @@ import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
// import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; // import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
interface DefaultContext { interface DefaultContext {
Component: FunctionalComponent & { type: { [key: string]: any } }; Component: FunctionalComponent & { type: Indexable };
route: RouteLocation; route: RouteLocation;
} }
......
...@@ -40,7 +40,7 @@ export function createPermissionGuard(router: Router) { ...@@ -40,7 +40,7 @@ export function createPermissionGuard(router: Router) {
return; return;
} }
// redirect login page // redirect login page
const redirectData: { path: string; replace: boolean; query?: { [key: string]: string } } = { const redirectData: { path: string; replace: boolean; query?: Indexable<string> } = {
path: LOGIN_PATH, path: LOGIN_PATH,
replace: true, replace: true,
}; };
......
...@@ -48,7 +48,7 @@ export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> { ...@@ -48,7 +48,7 @@ export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
component?: Component | string; component?: Component | string;
components?: Component; components?: Component;
children?: AppRouteRecordRaw[]; children?: AppRouteRecordRaw[];
props?: Record<string, any>; props?: Recordable;
fullPath?: string; fullPath?: string;
} }
export interface MenuTag { export interface MenuTag {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
export const HEADER_PRESET_BG_COLOR_LIST: string[] = [ export const HEADER_PRESET_BG_COLOR_LIST: string[] = [
'#ffffff', '#ffffff',
'#009688', '#009688',
'#18bc9c', '#5172DC',
'#1E9FFF', '#1E9FFF',
'#018ffb', '#018ffb',
'#409eff', '#409eff',
......
...@@ -15,18 +15,20 @@ declare function parseInt(s: string | number, radix?: number): number; ...@@ -15,18 +15,20 @@ declare function parseInt(s: string | number, radix?: number): number;
declare function parseFloat(string: string | number): number; declare function parseFloat(string: string | number): number;
declare type Dictionary<T> = Record<string, T>;
declare type Nullable<T> = T | null; declare type Nullable<T> = T | null;
declare type NonNullable<T> = T extends null | undefined ? never : T;
declare type RefType<T> = T | null; declare type RefType<T> = T | null;
declare type CustomizedHTMLElement<T> = HTMLElement & T; declare type CustomizedHTMLElement<T> = HTMLElement & T;
declare type Indexable<T = any> = { declare type Indexable<T extends any = any> = {
[key: string]: T; [key: string]: T;
}; };
declare type Recordable<T extends any = any> = Record<string, T>;
declare type Hash<T> = Indexable<T>; declare type Hash<T> = Indexable<T>;
declare type DeepPartial<T> = { declare type DeepPartial<T> = {
...@@ -59,3 +61,5 @@ declare interface ComponentElRef<T extends HTMLElement = HTMLDivElement> { ...@@ -59,3 +61,5 @@ declare interface ComponentElRef<T extends HTMLElement = HTMLDivElement> {
declare type ComponentRef<T extends HTMLElement = HTMLDivElement> = ComponentElRef<T> | null; declare type ComponentRef<T extends HTMLElement = HTMLDivElement> = ComponentElRef<T> | null;
declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>; declare type ElRef<T extends HTMLElement = HTMLDivElement> = Nullable<T>;
type IsSame<A, B> = A | B extends A & B ? true : false;
...@@ -7,8 +7,8 @@ const ls = createStorage(localStorage); ...@@ -7,8 +7,8 @@ const ls = createStorage(localStorage);
const ss = createStorage(); const ss = createStorage();
interface CacheStore { interface CacheStore {
local: Record<string, any>; local: Recordable;
session: Record<string, any>; session: Recordable;
} }
/** /**
......
...@@ -35,7 +35,7 @@ export function extendSlots(slots: Slots, excludeKeys: string[] = []) { ...@@ -35,7 +35,7 @@ export function extendSlots(slots: Slots, excludeKeys: string[] = []) {
} }
// Get events on attrs // Get events on attrs
export function getListeners(attrs: Record<string, unknown>) { export function getListeners(attrs: Recordable<unknown>) {
const listeners: any = {}; const listeners: any = {};
Object.keys(attrs).forEach((key) => { Object.keys(attrs).forEach((key) => {
if (/^on/.test(key)) { if (/^on/.test(key)) {
......
...@@ -9,6 +9,7 @@ import { ...@@ -9,6 +9,7 @@ import {
reactive, reactive,
ComponentInternalInstance, ComponentInternalInstance,
} from 'vue'; } from 'vue';
import { error } from '../log';
export function explicitComputed<T, S>(source: WatchSource<S>, fn: () => T) { export function explicitComputed<T, S>(source: WatchSource<S>, fn: () => T) {
const v = reactive<any>({ value: fn() }); const v = reactive<any>({ value: fn() });
...@@ -39,6 +40,6 @@ export function tryTsxEmit<T extends any = ComponentInternalInstance>( ...@@ -39,6 +40,6 @@ export function tryTsxEmit<T extends any = ComponentInternalInstance>(
export function isInSetup() { export function isInSetup() {
if (!getCurrentInstance()) { if (!getCurrentInstance()) {
throw new Error('Please put useForm function in the setup function!'); error('Please put useForm function in the setup function!');
} }
} }
...@@ -35,7 +35,7 @@ export interface Result<T = any> { ...@@ -35,7 +35,7 @@ export interface Result<T = any> {
// multipart/form-data:上传文件 // multipart/form-data:上传文件
export interface UploadFileParams { export interface UploadFileParams {
// 其他参数 // 其他参数
data?: { [key: string]: any }; data?: Indexable;
// 文件参数的接口字段名 // 文件参数的接口字段名
name?: string; name?: string;
// 文件 // 文件
......
...@@ -38,7 +38,7 @@ export function setObjToUrlParams(baseUrl: string, obj: any): string { ...@@ -38,7 +38,7 @@ export function setObjToUrlParams(baseUrl: string, obj: any): string {
return url; return url;
} }
export function deepMerge<T = any>(src: any, target: any): T { export function deepMerge<T = any>(src: any = {}, target: any = {}): T {
let key: string; let key: string;
for (key in target) { for (key in target) {
src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]); src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]);
......
...@@ -3,3 +3,7 @@ const projectName = import.meta.env.VITE_GLOB_APP_TITLE; ...@@ -3,3 +3,7 @@ const projectName = import.meta.env.VITE_GLOB_APP_TITLE;
export function warn(message: string) { export function warn(message: string) {
console.warn(`[${projectName} warn]:${message}`); console.warn(`[${projectName} warn]:${message}`);
} }
export function error(message: string) {
throw new Error(`[${projectName} error]:${message}`);
}
...@@ -84,12 +84,15 @@ ...@@ -84,12 +84,15 @@
required: true, required: true,
// @ts-ignore // @ts-ignore
validator: async (rule, value) => { validator: async (rule, value) => {
if (!value) {
return Promise.reject('值不能为空');
}
if (value === '1') { if (value === '1') {
return Promise.reject('值不能为1'); return Promise.reject('值不能为1');
} }
return Promise.resolve(); return Promise.resolve();
}, },
trigger: 'blur', trigger: 'change',
}, },
], ],
}, },
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论