Skip to content

Commit 5bbe27c

Browse files
authored
feat(Form): 增加校验消息提示类型参数 (DevCloudFE#570)
* feat(Form): 增加消息提示类型参数
1 parent 37e2214 commit 5bbe27c

File tree

18 files changed

+143
-1058
lines changed

18 files changed

+143
-1058
lines changed

packages/devui-vue/devui/form/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import FormLabel from './src/components/form-label/form-label';
44
import FormItem from './src/components/form-item/form-item';
55
import FormControl from './src/components/form-control/form-control';
66
import FormOperation from './src/components/form-operation/form-operation';
7-
import dValidateRules from './src/directives/d-validate-rules';
87

98
export { Form, FormLabel, FormItem, FormControl, FormOperation };
109

@@ -18,7 +17,6 @@ export default {
1817
status: '75%',
1918
install(app: App): void {
2019
app.component(Form.name, Form);
21-
app.directive('d-validate-rules', dValidateRules);
2220
app.component(FormLabel.name, FormLabel);
2321
app.component(FormItem.name, FormItem);
2422
app.component(FormControl.name, FormControl);

packages/devui-vue/devui/form/src/components/form-control/form-control.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
.devui-form__control-container {
1717
position: relative;
1818

19+
// 临时解决popover影响输入控件宽度的问题
20+
.devui-popover-reference {
21+
width: 100%;
22+
}
23+
1924
.devui-form__control-feedback {
2025
position: absolute;
2126
top: 50%;

packages/devui-vue/devui/form/src/components/form-control/form-control.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { defineComponent, ref } from 'vue';
22
import type { SetupContext } from 'vue';
3-
import { uniqueId } from 'lodash';
43
import { formControlProps, FormControlProps } from './form-control-types';
4+
import { Popover } from '../../../../popover';
55
import { useNamespace } from '../../../../shared/hooks/use-namespace';
66
import { useFormControl, useFormControlValidate } from './use-form-control';
77
import './form-control.scss';
@@ -11,20 +11,26 @@ export default defineComponent({
1111
props: formControlProps,
1212
setup(props: FormControlProps, ctx: SetupContext) {
1313
const formControl = ref();
14-
const uid = uniqueId('dfc-');
1514
const ns = useNamespace('form');
1615
const { controlClasses, controlContainerClasses } = useFormControl(props);
17-
const { errorMessage } = useFormControlValidate();
16+
const { showPopover, showMessage, errorMessage, popPosition } = useFormControlValidate();
1817

1918
return () => (
20-
<div class={controlClasses.value} ref={formControl} data-uid={uid}>
19+
<div class={controlClasses.value} ref={formControl}>
2120
<div class={controlContainerClasses.value}>
22-
<div class={ns.e('control-content')} id={uid}>
23-
{ctx.slots.default?.()}
24-
</div>
21+
<Popover
22+
is-open={showPopover.value}
23+
trigger="manually"
24+
content={errorMessage.value}
25+
pop-type="error"
26+
position={popPosition.value}>
27+
{{
28+
reference: () => <div class={ns.e('control-content')}>{ctx.slots.default?.()}</div>,
29+
}}
30+
</Popover>
2531
</div>
2632
<div class={ns.e('control-info')}>
27-
{errorMessage.value && <div class="error-message">{errorMessage.value}</div>}
33+
{showMessage.value && <div class="error-message">{errorMessage.value}</div>}
2834
{props.extraInfo && <div class={ns.e('control-extra')}>{props.extraInfo}</div>}
2935
</div>
3036
</div>
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1-
import { computed, reactive, inject, toRefs } from 'vue';
1+
import { computed, inject, toRefs } from 'vue';
22
import { FORM_TOKEN, FormContext } from '../../form-types';
33
import { FormControlProps, UseFormControl } from './form-control-types';
44
import { FormItemContext, FORM_ITEM_TOKEN } from '../form-item/form-item-types';
55
import { useNamespace } from '../../../../shared/hooks/use-namespace';
66

77
export function useFormControl(props: FormControlProps): UseFormControl {
88
const formContext = inject(FORM_TOKEN) as FormContext;
9-
const labelData = reactive(formContext.labelData);
109
const ns = useNamespace('form');
1110
const { feedbackStatus } = toRefs(props);
1211

1312
const controlClasses = computed(() => ({
1413
[`${ns.e('control')}`]: true,
15-
[`${ns.em('control', 'horizontal')}`]: labelData.layout === 'horizontal',
14+
[`${ns.em('control', 'horizontal')}`]: formContext.layout === 'horizontal',
1615
}));
1716

1817
const controlContainerClasses = computed(() => ({
1918
[`${ns.e('control-container')}`]: true,
20-
[`${ns.em('control-container', 'horizontal')}`]: labelData.layout === 'horizontal',
19+
[`${ns.em('control-container', 'horizontal')}`]: formContext.layout === 'horizontal',
2120
[`${ns.em('control-container', 'has-feedback')}`]: Boolean(feedbackStatus.value),
2221
[`${ns.em('control-container', 'feedback-error')}`]: Boolean(feedbackStatus.value === 'error'),
2322
}));
@@ -27,7 +26,10 @@ export function useFormControl(props: FormControlProps): UseFormControl {
2726

2827
export function useFormControlValidate() {
2928
const formItemContext = inject(FORM_ITEM_TOKEN) as FormItemContext;
29+
const showPopover = computed(() => formItemContext.messageType === 'popover' && formItemContext.validateState === 'error');
30+
const showMessage = computed(() => formItemContext.messageType === 'text' && formItemContext.validateState === 'error');
3031
const errorMessage = computed(() => formItemContext.validateMessage);
32+
const popPosition = computed(() => formItemContext.popPosition);
3133

32-
return { errorMessage };
34+
return { showPopover, showMessage, errorMessage, popPosition };
3335
}

packages/devui-vue/devui/form/src/components/form-item/form-item-types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@ import type { RuleItem, ValidateFieldsError } from 'async-validator';
22
import type { ComputedRef, ExtractPropTypes, PropType, InjectionKey, Ref } from 'vue';
33

44
export type FormItemValidateState = '' | 'error' | 'pending' | 'success';
5+
export type MessageType = 'popover' | 'text' | 'none';
6+
export type PopPosition =
7+
| 'top'
8+
| 'right'
9+
| 'bottom'
10+
| 'left'
11+
| 'top-start'
12+
| 'top-end'
13+
| 'right-start'
14+
| 'right-end'
15+
| 'bottom-start'
16+
| 'bottom-end'
17+
| 'left-start'
18+
| 'left-end';
519

620
export interface FormRuleItem extends RuleItem {
721
trigger?: Array<string>;
@@ -20,6 +34,12 @@ export const formItemProps = {
2034
type: Boolean,
2135
default: false,
2236
},
37+
messageType: {
38+
type: String as PropType<MessageType>,
39+
},
40+
popPosition: {
41+
type: Array as PropType<Array<PopPosition>>,
42+
},
2343
rules: {
2444
type: [Object, Array] as PropType<[FormRuleItem, Array<FormRuleItem>]>,
2545
},

packages/devui-vue/devui/form/src/components/form-item/form-item.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { defineComponent, onMounted, inject, reactive, toRefs, onBeforeUnmount, provide, toRef } from 'vue';
1+
import { defineComponent, onMounted, inject, reactive, toRefs, onBeforeUnmount, provide, computed } from 'vue';
22
import type { SetupContext } from 'vue';
3-
import { FORM_TOKEN } from '../../form-types';
3+
import { FormContext, FORM_TOKEN } from '../../form-types';
44
import { FormItemContext, formItemProps, FormItemProps, FORM_ITEM_TOKEN } from './form-item-types';
55
import { useFormItem, useFormItemRule, useFormItemValidate } from './use-form-item';
66
import './form-item.scss';
@@ -9,12 +9,17 @@ export default defineComponent({
99
name: 'DFormItem',
1010
props: formItemProps,
1111
setup(props: FormItemProps, ctx: SetupContext) {
12-
const formContext = inject(FORM_TOKEN);
12+
const formContext = inject(FORM_TOKEN) as FormContext;
13+
const { messageType: itemMessageType, popPosition: itemPopPosition, ...otherProps } = toRefs(props);
14+
const messageType = computed(() => itemMessageType?.value || formContext.messageType);
15+
const popPosition = computed(() => itemPopPosition?.value || formContext.popPosition);
1316
const { _rules } = useFormItemRule(props);
1417
const { validateState, validateMessage, validate } = useFormItemValidate(props, _rules);
15-
const { itemClasses, isRequired } = useFormItem(_rules, validateState);
18+
const { itemClasses, isRequired } = useFormItem(messageType, _rules, validateState);
1619
const context: FormItemContext = reactive({
17-
...toRefs(props),
20+
...otherProps,
21+
messageType,
22+
popPosition,
1823
isRequired,
1924
validateState,
2025
validateMessage,

packages/devui-vue/devui/form/src/components/form-item/use-form-item.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { computed, reactive, inject, ref } from 'vue';
1+
import { computed, inject, ref } from 'vue';
22
import { castArray, get, isFunction } from 'lodash';
33
import Schema from 'async-validator';
44
import type { ComputedRef, Ref } from 'vue';
@@ -11,18 +11,22 @@ import {
1111
FormValidateCallback,
1212
FormRuleItem,
1313
UseFormItemValidate,
14+
MessageType,
1415
} from './form-item-types';
1516
import { useNamespace } from '../../../../shared/hooks/use-namespace';
1617

17-
export function useFormItem(_rules: ComputedRef<FormRuleItem[]>, validateState: Ref<FormItemValidateState>): UseFormItem {
18-
const formContext = reactive(inject(FORM_TOKEN) as FormContext);
19-
const labelData = reactive(formContext.labelData);
18+
export function useFormItem(
19+
messageType: ComputedRef<MessageType>,
20+
_rules: ComputedRef<FormRuleItem[]>,
21+
validateState: Ref<FormItemValidateState>
22+
): UseFormItem {
23+
const formContext = inject(FORM_TOKEN) as FormContext;
2024
const ns = useNamespace('form');
2125

2226
const itemClasses = computed(() => ({
23-
[`${ns.em('item', 'horizontal')}`]: labelData.layout === 'horizontal',
24-
[`${ns.em('item', 'vertical')}`]: labelData.layout === 'vertical',
25-
[`${ns.em('item', 'error')}`]: validateState.value === 'error',
27+
[`${ns.em('item', 'horizontal')}`]: formContext.layout === 'horizontal',
28+
[`${ns.em('item', 'vertical')}`]: formContext.layout === 'vertical',
29+
[`${ns.em('item', 'error')}`]: messageType.value === 'text' && validateState.value === 'error',
2630
}));
2731

2832
const isRequired = computed(() => _rules.value.some((rule) => Boolean(rule.required)));
@@ -61,7 +65,7 @@ export function useFormItemValidate(props: FormItemProps, _rules: ComputedRef<Fo
6165
return typeof props.field === 'string' ? props.field : '';
6266
});
6367
const fieldValue = computed(() => {
64-
const formData = formContext.formData;
68+
const formData = formContext.data;
6569
if (!formData || !props.field) {
6670
return;
6771
}

packages/devui-vue/devui/form/src/components/form-label/use-form-label.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { UseFormLabel } from './form-label-types';
55
import { useNamespace } from '../../../../shared/hooks/use-namespace';
66

77
export function useFormLabel(): UseFormLabel {
8-
const { labelData } = inject(FORM_TOKEN) as FormContext;
8+
const formContext = inject(FORM_TOKEN) as FormContext;
99
const formItemContext = inject(FORM_ITEM_TOKEN) as FormItemContext;
1010
const ns = useNamespace('form');
1111

1212
const labelClasses = computed(() => ({
1313
[`${ns.e('label')}`]: true,
14-
[`${ns.em('label', 'vertical')}`]: labelData.layout === 'vertical',
15-
[`${ns.em('label', labelData.labelSize)}`]: labelData.layout === 'horizontal',
16-
[`${ns.em('label', labelData.labelAlign)}`]: labelData.layout === 'horizontal',
14+
[`${ns.em('label', 'vertical')}`]: formContext.layout === 'vertical',
15+
[`${ns.em('label', formContext.labelSize)}`]: formContext.layout === 'horizontal',
16+
[`${ns.em('label', formContext.labelAlign)}`]: formContext.layout === 'horizontal',
1717
}));
1818

1919
const labelInnerClasses = computed(() => ({

packages/devui-vue/devui/form/src/components/form-operation/form-operation.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
import { defineComponent, computed, reactive, inject } from 'vue';
1+
import { defineComponent, computed, inject } from 'vue';
22
import { FORM_TOKEN, FormContext, LabelSize } from '../../form-types';
33
import './form-operation.scss';
44

55
export default defineComponent({
66
name: 'DFormOperation',
77
setup(props, ctx) {
8-
const formContext = reactive(inject(FORM_TOKEN) as FormContext);
9-
const labelData = reactive(formContext.labelData);
8+
const formContext = inject(FORM_TOKEN) as FormContext;
109
const LabelSizeMap: Record<LabelSize, number> = {
1110
sm: 80,
1211
md: 100,
1312
lg: 150,
1413
};
1514
const styles = computed(() => ({
16-
marginLeft: labelData.layout === 'horizontal' ? `${LabelSizeMap[labelData.labelSize] + 16}px` : undefined,
15+
marginLeft: formContext.layout === 'horizontal' ? `${LabelSizeMap[formContext.labelSize] + 16}px` : undefined,
1716
}));
1817
return () => (
1918
<div class="devui-form-operation" style={styles.value}>

0 commit comments

Comments
 (0)