feat(form): add merge form functionality (#4495)
* feat: captcha example * fix: fix lint errors * chore: event handling and methods * chore: add accessibility features ARIA labels and roles * refactor: refactor code structure and improve captcha demo page * feat: add captcha internationalization * chore: 适配时间戳国际化展示 * fix: 1. 添加点击位置边界校验,防止点击外部导致x,y误差。2. 演示页面宽度过长添加滚动条。3. 添加hooks * feat: sync test * feat: 添加合并表单功能 * fix: 修复上一步不展示问题 --------- Co-authored-by: vince <vince292007@gmail.com>pull/48/MERGE
parent
476aa068d7
commit
fdc5b02c30
|
@ -123,6 +123,47 @@ export class FormApi {
|
|||
return form.values;
|
||||
}
|
||||
|
||||
merge(formApi: FormApi) {
|
||||
const chain = [this, formApi];
|
||||
const proxy = new Proxy(formApi, {
|
||||
get(target: any, prop: any) {
|
||||
if (prop === 'merge') {
|
||||
return (nextFormApi: FormApi) => {
|
||||
chain.push(nextFormApi);
|
||||
return proxy;
|
||||
};
|
||||
}
|
||||
if (prop === 'submitAllForm') {
|
||||
return async (needMerge: boolean = true) => {
|
||||
try {
|
||||
const results = await Promise.all(
|
||||
chain.map(async (api) => {
|
||||
const form = await api.getForm();
|
||||
const validateResult = await api.validate();
|
||||
if (!validateResult.valid) {
|
||||
return;
|
||||
}
|
||||
const rawValues = toRaw(form.values || {});
|
||||
return rawValues;
|
||||
}),
|
||||
);
|
||||
if (needMerge) {
|
||||
const mergedResults = Object.assign({}, ...results);
|
||||
return mergedResults;
|
||||
}
|
||||
return results;
|
||||
} catch (error) {
|
||||
console.error('Validation error:', error);
|
||||
}
|
||||
};
|
||||
}
|
||||
return target[prop];
|
||||
},
|
||||
});
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
mount(formActions: FormActions) {
|
||||
if (!this.isMounted) {
|
||||
Object.assign(this.form, formActions);
|
||||
|
|
|
@ -7,6 +7,7 @@ export function useCaptchaPoints() {
|
|||
function addPoint(point: CaptchaPoint) {
|
||||
points.push(point);
|
||||
}
|
||||
|
||||
function clearPoints() {
|
||||
points.splice(0, points.length);
|
||||
}
|
||||
|
|
|
@ -79,7 +79,8 @@
|
|||
"rules": "Form Rules",
|
||||
"dynamic": "Dynamic Form",
|
||||
"custom": "Custom Component",
|
||||
"api": "Api"
|
||||
"api": "Api",
|
||||
"merge": "Merge Form"
|
||||
},
|
||||
"captcha": {
|
||||
"title": "Captcha",
|
||||
|
|
|
@ -79,7 +79,8 @@
|
|||
"rules": "表单校验",
|
||||
"dynamic": "动态表单",
|
||||
"custom": "自定义组件",
|
||||
"api": "Api"
|
||||
"api": "Api",
|
||||
"merge": "合并表单"
|
||||
},
|
||||
"captcha": {
|
||||
"title": "验证码",
|
||||
|
|
|
@ -99,6 +99,14 @@ const routes: RouteRecordRaw[] = [
|
|||
title: $t('page.examples.form.api'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormMergeExample',
|
||||
path: '/examples/form/merge',
|
||||
component: () => import('#/views/examples/form/merge.vue'),
|
||||
meta: {
|
||||
title: $t('page.examples.form.merge'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button, Card, message, Step, Steps, Switch } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter';
|
||||
|
||||
const currentTab = ref(0);
|
||||
function onFirstSubmit(values: Record<string, any>) {
|
||||
message.success({
|
||||
content: `form1 values: ${JSON.stringify(values)}`,
|
||||
});
|
||||
currentTab.value = 1;
|
||||
}
|
||||
function onSecondReset() {
|
||||
currentTab.value = 0;
|
||||
}
|
||||
function onSecondSubmit(values: Record<string, any>) {
|
||||
message.success({
|
||||
content: `form2 values: ${JSON.stringify(values)}`,
|
||||
});
|
||||
}
|
||||
|
||||
const [FirstForm, firstFormApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
handleSubmit: onFirstSubmit,
|
||||
layout: 'horizontal',
|
||||
resetButtonOptions: {
|
||||
show: false,
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'formFirst',
|
||||
label: '表单1字段',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
submitButtonOptions: {
|
||||
text: '下一步',
|
||||
},
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-1 lg:grid-cols-1',
|
||||
});
|
||||
const [SecondForm, secondFormApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
handleReset: onSecondReset,
|
||||
handleSubmit: onSecondSubmit,
|
||||
layout: 'horizontal',
|
||||
resetButtonOptions: {
|
||||
text: '上一步',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'formSecond',
|
||||
label: '表单2字段',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-1 lg:grid-cols-1',
|
||||
});
|
||||
const needMerge = ref(true);
|
||||
async function handleMergeSubmit() {
|
||||
const values = await firstFormApi
|
||||
.merge(secondFormApi)
|
||||
.submitAllForm(needMerge.value);
|
||||
message.success({
|
||||
content: `merged form values: ${JSON.stringify(values)}`,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page
|
||||
description="表单组件合并示例:在某些场景下,例如分步表单,需要合并多个表单并统一提交。默认情况下,使用 Object.assign 规则合并表单。如果需要特殊处理数据,可以传入 false。"
|
||||
title="表单组件"
|
||||
>
|
||||
<Card title="基础示例">
|
||||
<template #extra>
|
||||
<Switch
|
||||
v-model:checked="needMerge"
|
||||
checked-children="开启字段合并"
|
||||
class="mr-4"
|
||||
un-checked-children="关闭字段合并"
|
||||
/>
|
||||
<Button type="primary" @click="handleMergeSubmit">合并提交</Button>
|
||||
</template>
|
||||
<div class="mx-auto max-w-lg">
|
||||
<Steps :current="currentTab" class="steps">
|
||||
<Step title="表单1" />
|
||||
<Step title="表单2" />
|
||||
</Steps>
|
||||
<div class="p-20">
|
||||
<FirstForm v-show="currentTab === 0" />
|
||||
<SecondForm v-show="currentTab === 1" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
Loading…
Reference in New Issue