Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: form fieldMappingTime improve and modelPropName support #5335

Merged
merged 2 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion docs/src/components/common-ui/vben-form.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,18 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
| collapsed | 是否折叠,在`showCollapseButton`为`true`时生效 | `boolean` | `false` |
| collapseTriggerResize | 折叠时,触发`resize`事件 | `boolean` | `false` |
| collapsedRows | 折叠时保持的行数 | `number` | `1` |
| fieldMappingTime | 用于将表单内时间区域组件的数组值映射成 2 个字段 | `[string, [string, string], string?][]` | - |
| fieldMappingTime | 用于将表单内的数组值值映射成 2 个字段 | `[string, [string, string],Nullable<string>?][]` | - |
| commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - |
| schema | 表单项的每一项配置 | `FormSchema[]` | - |
| submitOnEnter | 按下回车健时提交表单 | `boolean` | false |
| submitOnChange | 字段值改变时提交表单(内部防抖,这个属性一般用于表格的搜索表单) | `boolean` | false |

::: tip fieldMappingTime

此属性用于将表单内的数组值映射成 2 个字段,例如:`[['timeRange', ['startTime', 'endTime'], 'YYYY-MM-DD']]`,`timeRange`应当是一个至少具有2个成员的数组类型的值。Form会将`timeRange`的值前两个值分别按照格式掩码`YYYY-MM-DD`格式化后映射到`startTime`和`endTime`字段上。如果明确地将格式掩码设为null,则原值映射而不进行格式化(适用于非日期时间字段)。

:::

### TS 类型说明

::: details ActionButtonOptions
Expand Down Expand Up @@ -406,6 +412,11 @@ export interface FormCommonConfig {
* 所有表单项的label宽度
*/
labelWidth?: number;
/**
* 所有表单项的model属性名。使用自定义组件时可通过此配置指定组件的model属性名。已经在modelPropNameMap中注册的组件不受此配置影响
* @default "modelValue"
*/
modelPropName?: string;
/**
* 所有表单项的wrapper样式
*/
Expand Down
26 changes: 15 additions & 11 deletions packages/@core/ui-kit/form-ui/src/form-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,17 +368,21 @@ export class FormApi {
}

const [startTime, endTime] = values[field];
const [startTimeFormat, endTimeFormat] = Array.isArray(format)
? format
: [format, format];

values[startTimeKey] = startTime
? formatDate(startTime, startTimeFormat)
: undefined;
values[endTimeKey] = endTime
? formatDate(endTime, endTimeFormat)
: undefined;

if (format === null) {
values[startTimeKey] = startTime;
values[endTimeKey] = endTime;
} else {
const [startTimeFormat, endTimeFormat] = Array.isArray(format)
? format
: [format, format];

values[startTimeKey] = startTime
? formatDate(startTime, startTimeFormat)
: undefined;
values[endTimeKey] = endTime
? formatDate(endTime, endTimeFormat)
: undefined;
}
// delete values[field];
Reflect.deleteProperty(values, field);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const {
label,
labelClass,
labelWidth,
modelPropName,
renderComponentContent,
rules,
} = defineProps<
Expand Down Expand Up @@ -202,9 +203,9 @@ function fieldBindEvent(slotProps: Record<string, any>) {
const modelValue = slotProps.componentField.modelValue;
const handler = slotProps.componentField['onUpdate:modelValue'];

const bindEventField = isString(component)
? componentBindEventMap.value?.[component]
: null;
const bindEventField =
modelPropName ||
(isString(component) ? componentBindEventMap.value?.[component] : null);

let value = modelValue;
// antd design 的一些组件会传递一个 event 对象
Expand Down
2 changes: 2 additions & 0 deletions packages/@core/ui-kit/form-ui/src/form-render/form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ const computedSchema = computed(
hideRequiredMark = false,
labelClass = '',
labelWidth = 100,
modelPropName = '',
wrapperClass = '',
} = mergeWithArrayOverride(props.commonConfig, props.globalCommonConfig);
return (props.schema || []).map((schema, index) => {
Expand All @@ -118,6 +119,7 @@ const computedSchema = computed(
hideLabel,
hideRequiredMark,
labelWidth,
modelPropName,
wrapperClass,
...schema,
commonComponentProps: componentProps,
Expand Down
11 changes: 8 additions & 3 deletions packages/@core/ui-kit/form-ui/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { ZodTypeAny } from 'zod';
import type { Component, HtmlHTMLAttributes, Ref } from 'vue';

import type { VbenButtonProps } from '@vben-core/shadcn-ui';
import type { ClassType } from '@vben-core/typings';
import type { ClassType, Nullable } from '@vben-core/typings';

import type { FormApi } from './form-api';

Expand Down Expand Up @@ -197,6 +197,11 @@ export interface FormCommonConfig {
* 所有表单项的label宽度
*/
labelWidth?: number;
/**
* 所有表单项的model属性名
* @default "modelValue"
*/
modelPropName?: string;
/**
* 所有表单项的wrapper样式
*/
Expand All @@ -219,7 +224,7 @@ export type HandleResetFn = (
export type FieldMappingTime = [
string,
[string, string],
([string, string] | string)?,
([string, string] | Nullable<string>)?,
][];

export interface FormSchema<
Expand Down Expand Up @@ -330,7 +335,7 @@ export interface VbenFormProps<
*/
actionWrapperClass?: ClassType;
/**
* 表单字段映射成时间格式
* 表单字段映射
*/
fieldMappingTime?: FieldMappingTime;
/**
Expand Down
6 changes: 5 additions & 1 deletion packages/styles/src/antd/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
.form-valid-error {
/** select 选择器的样式 */

.ant-select .ant-select-selector {
.ant-select:not(.valid-success) .ant-select-selector:not(.valid-success) {
border-color: hsl(var(--destructive)) !important;
}

Expand All @@ -39,6 +39,10 @@
border-color: hsl(var(--destructive));
box-shadow: 0 0 0 2px rgb(255 38 5 / 6%);
}

.ant-input:not(.valid-success) {
border-color: hsl(var(--destructive)) !important;
}
}

/** 区间选择器下面来回切换时的样式 */
Expand Down
28 changes: 25 additions & 3 deletions playground/src/views/examples/form/custom.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<script lang="ts" setup>
import { h } from 'vue';
import { h, markRaw } from 'vue';

import { Page } from '@vben/common-ui';

import { Card, Input, message } from 'ant-design-vue';

import { useVbenForm } from '#/adapter/form';
import { useVbenForm, z } from '#/adapter/form';

import TwoFields from './modules/two-fields.vue';

const [Form] = useVbenForm({
// 所有表单项共用,可单独在表单内覆盖
Expand All @@ -16,6 +18,7 @@ const [Form] = useVbenForm({
},
labelClass: 'w-2/6',
},
fieldMappingTime: [['field4', ['areaCode', 'phone'], null]],
// 提交函数
handleSubmit: onSubmit,
// 垂直布局,label和input在不同行,值为vertical
Expand All @@ -39,9 +42,10 @@ const [Form] = useVbenForm({
}),
},
{
component: h(Input, { placeholder: '请输入' }),
component: h(Input, { placeholder: '请输入Field2' }),
fieldName: 'field2',
label: '自定义组件',
modelPropName: 'value',
rules: 'required',
},
{
Expand All @@ -50,6 +54,24 @@ const [Form] = useVbenForm({
label: '自定义组件(slot)',
rules: 'required',
},
{
component: markRaw(TwoFields),
defaultValue: [undefined, '188'],
disabledOnChangeListener: false,
fieldName: 'field4',
formItemClass: 'col-span-1',
label: '组合字段',
rules: z
.array(z.string().optional())
.length(2, '请输入手机号码')
.refine((v) => !!v[0], {
message: '请选择区号',
})
.refine((v) => v[1]?.match(/^1[3-9]\d{9}$/), {
// 使用全角空格占位,将错误提示文字挤到手机号码输入框的下面
message: '       号码格式不正确',
}),
},
],
// 中屏一行显示2个,小屏一行显示1个
wrapperClass: 'grid-cols-1 md:grid-cols-2',
Expand Down
40 changes: 40 additions & 0 deletions playground/src/views/examples/form/modules/two-fields.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script lang="ts" setup>
import { Input, Select } from 'ant-design-vue';

const emit = defineEmits(['blur', 'change', 'change']);
mynetfan marked this conversation as resolved.
Show resolved Hide resolved

const modelValue = defineModel<[string, string]>({
default: ['+85', ''],
});

function onChange() {
emit('change', modelValue.value);
}
</script>
<template>
<div class="flex w-full gap-1">
<Select
v-model:value="modelValue[0]"
class="w-[80px]"
show-search
placeholder="区码"
:class="{ 'valid-success': !!modelValue[0] }"
@blur="emit('blur')"
@change="onChange"
>
<Select.Option value="+82">+82</Select.Option>
<Select.Option value="+85">+85</Select.Option>
<Select.Option value="+86">+86</Select.Option>
</Select>
<Input
placeholder="请输入11位手机号码"
class="flex-1"
:class="{ 'valid-success': modelValue[1]?.match(/^1[3-9]\d{9}$/) }"
v-model:value="modelValue[1]"
:maxlength="11"
type="tel"
@blur="emit('blur')"
@change="onChange"
/>
</div>
</template>
Loading