{currentState.result && (
diff --git a/mobile-ui/src/components/flow/domain/FlowEventContext.ts b/mobile-ui/src/components/flow/domain/FlowEventContext.ts
index 71f0f0b7..2abeda57 100644
--- a/mobile-ui/src/components/flow/domain/FlowEventContext.ts
+++ b/mobile-ui/src/components/flow/domain/FlowEventContext.ts
@@ -7,6 +7,7 @@ import {FlowUser} from "@/components/flow/types";
import {FlowSubmitResultParser} from "@/components/flow/domain/FlowResultParser";
import {UserSelectMode} from "@/components/flow/store/FlowSlice";
import {FlowTriggerContext} from "@/components/flow/domain/FlowTriggerContext";
+import FormInstance from "@/components/form/domain/FormInstance";
/**
* 流程的事件控制上下文对象
@@ -15,28 +16,28 @@ export class FlowEventContext {
private readonly flowRecordContext: FlowRecordContext;
private readonly flowTriggerContext: FlowTriggerContext;
- private readonly flowAction: React.RefObject
;
- private readonly opinionAction: React.RefObject;
+ private readonly flowInstance: FormInstance;
+ private readonly opinionInstance: FormInstance;
private readonly flowStateContext: FlowStateContext;
constructor(flowViewContext: FlowRecordContext,
flowTriggerContext:FlowTriggerContext,
- flowAction: React.RefObject,
- opinionAction: React.RefObject,
+ flowInstance: FormInstance,
+ opinionInstance: FormInstance,
flowStateContext: FlowStateContext) {
this.flowRecordContext = flowViewContext;
this.flowTriggerContext = flowTriggerContext;
- this.flowAction = flowAction;
- this.opinionAction = opinionAction;
+ this.flowInstance = flowInstance;
+ this.opinionInstance = opinionInstance;
this.flowStateContext = flowStateContext;
}
private getRequestBody = () => {
- const formData = this.flowAction.current?.getFieldsValue();
+ const formData = this.opinionInstance.getFieldsValue();
const flowData = this.flowRecordContext.getFlowFormParams();
const workCode = this.flowRecordContext.getWorkCode();
const recordId = this.flowStateContext.getRecordId();
- const advice = this.opinionAction.current?.getFieldsValue();
+ const advice = this.opinionInstance.getFieldsValue();
return {
recordId,
@@ -51,8 +52,8 @@ export class FlowEventContext {
private validateForm = async () => {
- const formState = await this.flowAction.current?.validate();
- const opinionState = await this.opinionAction.current?.validate();
+ const formState = await this.flowInstance.validate();
+ const opinionState = await this.opinionInstance.validate();
return formState && opinionState;
}
diff --git a/mobile-ui/src/components/flow/types.ts b/mobile-ui/src/components/flow/types.ts
index 4878a57f..64d066a9 100644
--- a/mobile-ui/src/components/flow/types.ts
+++ b/mobile-ui/src/components/flow/types.ts
@@ -1,5 +1,5 @@
import React from "react";
-import {FormAction} from "@/components/form";
+import FormInstance from "@/components/form/domain/FormInstance";
// 流程图中线的类型
@@ -106,7 +106,7 @@ export interface FlowFormViewProps {
// 表单数据
data: FlowFormParams;
// 表单控制对象
- formAction: React.RefObject;
+ form: FormInstance;
// 数据版本
dataVersion?: number;
}
diff --git a/mobile-ui/src/components/flow/view/index.tsx b/mobile-ui/src/components/flow/view/index.tsx
index 986a5be3..2cdd5cf5 100644
--- a/mobile-ui/src/components/flow/view/index.tsx
+++ b/mobile-ui/src/components/flow/view/index.tsx
@@ -6,12 +6,12 @@ import {Skeleton} from "antd-mobile";
import {FlowRecordContext} from "@/components/flow/domain/FlowRecordContext";
import {FlowEventContext} from "@/components/flow/domain/FlowEventContext";
import {detail} from "@/api/flow";
-import {FormAction} from "@/components/form";
import {FlowStateContext} from "@/components/flow/domain/FlowStateContext";
import FlowPage from "@/components/flow/components/FlowPage";
import {FlowTriggerContext} from "@/components/flow/domain/FlowTriggerContext";
import {FlowButtonClickContext} from "@/components/flow/domain/FlowButtonClickContext";
import "./index.scss";
+import FormInstance from "@/components/form/domain/FormInstance";
// 流程视图上下文属性
interface FlowViewReactContextProps {
@@ -26,9 +26,9 @@ interface FlowViewReactContextProps {
// 流程按钮点击触发控制器上下文对象
flowButtonClickContext: FlowButtonClickContext;
// 表单操作对象
- formAction: React.RefObject;
+ formInstance: FormInstance;
// 审批意见操作对象
- opinionAction: React.RefObject;
+ opinionInstance: FormInstance;
}
export const FlowViewReactContext = createContext(null);
diff --git a/mobile-ui/src/components/form/common.tsx b/mobile-ui/src/components/form/common.tsx
index eee2e5bd..8f707c25 100644
--- a/mobile-ui/src/components/form/common.tsx
+++ b/mobile-ui/src/components/form/common.tsx
@@ -4,8 +4,8 @@ import {FormItemProps} from "@/components/form/types";
const formFieldInit = (props: FormItemProps,reloadOption?:()=>void) => {
const formContext = React.useContext(FormContext) || undefined;
- const formAction = formContext?.formAction;
- const validateContext = formContext?.validateContext;
+ const formAction = formContext?.getFormAction();
+ const validateContext = formContext?.getFormValidateContext();
const [random, setRandom] = React.useState(0);
const rules= props.required?[{required: true}]:[];
@@ -20,14 +20,14 @@ const formFieldInit = (props: FormItemProps,reloadOption?:()=>void) => {
}
}
}
- const reloadContext = formContext?.reloadContext;
+ const reloadContext = formContext?.getFormFieldReloadListenerContext();
if (reloadContext) {
reloadContext.addListener(props.name, () => {
setRandom(Math.random);
});
}
- const optionContext = formContext?.optionContext;
+ const optionContext = formContext?.getFormFieldOptionListenerContext();
if (optionContext) {
optionContext.addListener(props.name, () => {
if(reloadOption){
diff --git a/mobile-ui/src/components/form/domain/FormInstance.tsx b/mobile-ui/src/components/form/domain/FormInstance.tsx
new file mode 100644
index 00000000..475768e3
--- /dev/null
+++ b/mobile-ui/src/components/form/domain/FormInstance.tsx
@@ -0,0 +1,323 @@
+import {FormValidateContext} from "@/components/form/validate";
+import {FormFieldOptionListenerContext, FormFieldReloadListenerContext} from "@/components/form/listener";
+import {FormInstance as MobileFormInstance} from "rc-field-form/es/interface";
+import {FormField} from "@/components/form/types";
+import {NamePath} from "antd-mobile/es/components/form";
+import {Form as MobileForm, Toast} from "antd-mobile";
+import {FiledData, FormAction} from "@/components/form";
+import React from "react";
+
+class FormInstance {
+ private readonly validateContext: FormValidateContext;
+ private readonly reloadContext: FormFieldReloadListenerContext;
+ private readonly optionContext: FormFieldOptionListenerContext;
+ private readonly formInstance: MobileFormInstance;
+ private readonly formAction: FormAction;
+ private fields: FormField[];
+
+ private fieldsUpdateDispatch: React.Dispatch> | undefined;
+
+ public setFieldsUpdateDispatch = (fieldsUpdateDispatch: React.Dispatch>) => {
+ this.fieldsUpdateDispatch = fieldsUpdateDispatch;
+ }
+
+ private updateFields = (resetFields:(prevState: FormField[]) => FormField[]) => {
+ this.fields = resetFields(this.fields);
+ if (this.fieldsUpdateDispatch) {
+ this.fieldsUpdateDispatch(resetFields);
+ }
+ }
+
+ private namePathEqual = (name1: NamePath, name2: NamePath) => {
+ if (Array.isArray(name1) && Array.isArray(name2)) {
+ if (name1.length !== name2.length) {
+ return false;
+ }
+ for (let i = 0; i < name1.length; i++) {
+ if (name1[i] !== name2[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return name1 === name2;
+ }
+
+ public submit = async () => {
+ const res = await this.validateContext.validate(this.formAction);
+ if (res) {
+ this.formInstance.submit();
+ }
+ }
+
+ public reset = (values?: any) => {
+ this.formInstance.resetFields();
+ if (values) {
+ this.formInstance.setFieldsValue(values);
+ this.reloadContext.notifyAll();
+ }
+ }
+
+ public hidden = (name: NamePath) => {
+ if (this.fields.length == 0) {
+ Toast.show('表单项未加载');
+ return;
+ }
+ this.updateFields(prevFields => prevFields.map((field) => {
+ if (this.namePathEqual(field.props.name, name)) {
+ return {
+ ...field,
+ props: {
+ ...field.props,
+ hidden: true,
+ required: false
+ }
+ }
+ }
+ return field;
+ }));
+ this.validateContext.clear();
+ }
+
+ public required = (name: NamePath, required: boolean) => {
+ if (this.fields.length == 0) {
+ Toast.show('表单项未加载');
+ return;
+ }
+ this.updateFields(prevFields => prevFields.map((field) => {
+ if (this.namePathEqual(field.props.name,name)) {
+ return {
+ ...field,
+ props: {
+ ...field.props,
+ required: required
+ }
+ }
+ }
+ return field;
+ }));
+ this.validateContext.clear();
+ }
+
+ public show = (name: NamePath) => {
+ if (this.fields.length == 0) {
+ Toast.show('表单项未加载');
+ return;
+ }
+ this.updateFields(prevFields => prevFields.map((field) => {
+ if (this.namePathEqual(field.props.name,name)) {
+ return {
+ ...field,
+ props: {
+ ...field.props,
+ hidden: false
+ }
+ }
+ }
+ return field;
+ }));
+ this.validateContext.clear();
+ }
+
+ public disable = (name: NamePath) => {
+ if (this.fields.length == 0) {
+ Toast.show('表单项未加载');
+ return;
+ }
+ this.updateFields(prevFields => prevFields.map((field) => {
+ if (this.namePathEqual(field.props.name,name)) {
+ return {
+ ...field,
+ props: {
+ ...field.props,
+ disabled: true
+ }
+ }
+ }
+ return field;
+ }));
+ this.validateContext.clear();
+ }
+
+ public disableAll = () => {
+ if (this.fields.length == 0) {
+ Toast.show('表单项未加载');
+ return;
+ }
+ this.updateFields(prevFields => prevFields.map((field) => {
+ return {
+ ...field,
+ props: {
+ ...field.props,
+ disabled: true
+ }
+ }
+ }));
+ this.validateContext.clear();
+ }
+
+ public enable = (name: NamePath) => {
+ if (this.fields.length == 0) {
+ Toast.show('表单项未加载');
+ return;
+ }
+ this.updateFields(prevFields => prevFields.map((field) => {
+ if (this.namePathEqual(field.props.name,name)) {
+ return {
+ ...field,
+ props: {
+ ...field.props,
+ disabled: false
+ }
+ }
+ }
+ return field;
+ }));
+ this.validateContext.clear();
+ }
+
+ public enableAll = () => {
+ if (this.fields.length == 0) {
+ Toast.show('表单项未加载');
+ return;
+ }
+ this.updateFields(prevFields => prevFields.map((field) => {
+ return {
+ ...field,
+ props: {
+ ...field.props,
+ disabled: false
+ }
+ }
+ }));
+ this.validateContext.clear();
+ }
+
+ public remove = (name: NamePath) => {
+ if (this.fields.length == 0) {
+ Toast.show('表单项未加载');
+ return;
+ }
+ this.updateFields(prevFields => prevFields.filter((field) => !this.namePathEqual(field.props.name,name)));
+ this.validateContext.clear();
+ }
+
+ public create = (field: FormField, index?: number) => {
+ if (this.fields.length == 0) {
+ Toast.show('表单项未加载');
+ return;
+ }
+ this.updateFields(prevFields => {
+ const filteredFields = prevFields.filter((item) => item.props.name !== field.props.name);
+ if (index === undefined || index < 0) {
+ return [...filteredFields, field];
+ } else {
+ const newFields = [...filteredFields];
+ newFields.splice(index, 0, field);
+ return newFields;
+ }
+ });
+ this.validateContext.clear();
+ }
+
+ public getFieldValue = (name: NamePath) => {
+ return this.formInstance.getFieldValue(name);
+ }
+
+ public getFieldsValue = () => {
+ return this.formInstance.getFieldsValue();
+ }
+
+ public getFieldProps = (name: NamePath) => {
+ for (const field of this.fields) {
+ if (this.namePathEqual(field.props.name, name)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ public reloadOptions = (name: NamePath) => {
+ this.optionContext.notify(name);
+ }
+
+ public reloadAllOptions = () => {
+ this.optionContext.notifyAll();
+ }
+
+ public setFieldValue = (name: NamePath, value: any) => {
+ this.formInstance.setFieldValue(name, value);
+ this.reloadContext.notify(name);
+ this.validateContext?.validateField(name, this.formAction);
+ }
+
+ public setFieldsValue = (values: any) => {
+ this.formInstance.setFieldsValue(values);
+ this.reloadContext.notifyAll();
+ }
+
+ public setFields = (fields: FiledData[]) => {
+ this.formInstance.setFields(fields);
+ }
+
+ public validate = () => {
+ return this.validateContext.validate(this.formAction);
+ }
+
+ public resetFields = (fields:FormField[]) => {
+ this.fields = fields;
+ }
+
+ constructor() {
+ this.validateContext = new FormValidateContext();
+ this.reloadContext = new FormFieldReloadListenerContext();
+ this.optionContext = new FormFieldOptionListenerContext();
+ this.formInstance = MobileForm.useForm()[0];
+ this.fields = [];
+ this.formAction = {
+ submit: this.submit,
+ reset: this.reset,
+ hidden: this.hidden,
+ show: this.show,
+ remove: this.remove,
+ create: this.create,
+ disable: this.disable,
+ disableAll: this.disableAll,
+ enable: this.enable,
+ enableAll: this.enableAll,
+ required: this.required,
+ getFieldValue: this.getFieldValue,
+ getFieldsValue: this.getFieldsValue,
+ getFieldProps: this.getFieldProps,
+ reloadOptions: this.reloadOptions,
+ reloadAllOptions: this.reloadAllOptions,
+ setFieldValue: this.setFieldValue,
+ setFieldsValue: this.setFieldsValue,
+ setFields: this.setFields,
+ validate: this.validate,
+ }
+ }
+
+ public getFormAction = () => {
+ return this.formAction;
+ }
+
+ public getFormValidateContext = () => {
+ return this.validateContext;
+ }
+
+ public getFormFieldReloadListenerContext = () => {
+ return this.reloadContext;
+ }
+
+ public getFormFieldOptionListenerContext = () => {
+ return this.optionContext;
+ }
+
+ public getFormControlInstance = ():MobileFormInstance => {
+ return this.formInstance;
+ }
+
+}
+
+export default FormInstance;
diff --git a/mobile-ui/src/components/form/index.tsx b/mobile-ui/src/components/form/index.tsx
index 65f22379..123c4d50 100644
--- a/mobile-ui/src/components/form/index.tsx
+++ b/mobile-ui/src/components/form/index.tsx
@@ -1,11 +1,10 @@
import React, {useEffect} from "react";
-import {Form as MobileForm, Toast} from "antd-mobile";
+import {Form as MobileForm} from "antd-mobile";
import FormFactory from "@/components/form/factory";
import {FormField} from "@/components/form/types";
-import {FormValidateContext} from "@/components/form/validate";
import {NamePath} from "antd-mobile/es/components/form";
-import {FormFieldOptionListenerContext, FormFieldReloadListenerContext} from "@/components/form/listener";
import "./form.scss";
+import FormInstance from "@/components/form/domain/FormInstance";
export interface FiledData {
name: NamePath;
@@ -60,8 +59,6 @@ export interface FormProps {
loadFields?: ()=>Promise;
// 表单提交事件
onFinish?: (values: any) => Promise;
- // 表单控制对象
- actionRef?: React.Ref;
// form布局,默认vertical
layout?: 'horizontal' | 'vertical';
// children元素
@@ -70,283 +67,29 @@ export interface FormProps {
footer?: React.ReactNode;
// 初始化值
initialValues?: any;
+ // 表单控制对象
+ actionRef?: React.Ref;
+ // 表单实例
+ form?: FormInstance;
}
-interface FormContextProps {
- // form表单的控制对象
- formAction: FormAction;
- // 检验控制对象
- validateContext: FormValidateContext;
- // 表单刷新监听对象
- reloadContext:FormFieldReloadListenerContext;
- // 选项刷新监听对象
- optionContext:FormFieldOptionListenerContext;
-}
-
-export const FormContext = React.createContext(null);
-
-
-const namePathEqual = (name1: NamePath, name2: NamePath): boolean => {
- if (Array.isArray(name1) && Array.isArray(name2)) {
- if (name1.length !== name2.length) {
- return false;
- }
- for (let i = 0; i < name1.length; i++) {
- if (name1[i] !== name2[i]) {
- return false;
- }
- }
- return true;
- }
- return name1 === name2;
-}
-
-
-const Form: React.FC = (props) => {
-
- const [form] = MobileForm.useForm();
-
- const validateContext = new FormValidateContext();
- const reloadContext = new FormFieldReloadListenerContext();
- const optionContext = new FormFieldOptionListenerContext();
-
- const formAction = {
- submit: async () => {
- const res = await validateContext.validate(formAction);
- if (res) {
- form.submit();
- }
- },
-
- reset: (values?: any) => {
- reloadFields();
- form.resetFields();
- if (values) {
- form.setFieldsValue(values);
- reloadContext.notifyAll();
- }
- },
-
- hidden: (name: NamePath) => {
- if(fields.length==0){
- Toast.show('表单项未加载');
- return;
- }
- setFields(prevFields => prevFields.map((field) => {
- if (namePathEqual(field.props.name,name)) {
- return {
- ...field,
- props: {
- ...field.props,
- hidden: true,
- required: false
- }
- }
- }
- return field;
- }));
- validateContext.clear();
- },
-
- required:(name: NamePath,required:boolean) => {
- if(fields.length==0){
- Toast.show('表单项未加载');
- return;
- }
- setFields(prevFields => prevFields.map((field) => {
- if (namePathEqual(field.props.name,name)) {
- return {
- ...field,
- props: {
- ...field.props,
- required: required
- }
- }
- }
- return field;
- }));
- validateContext.clear();
- },
-
- show: (name: NamePath) => {
- if(fields.length==0){
- Toast.show('表单项未加载');
- return;
- }
- setFields(prevFields => prevFields.map((field) => {
- if (namePathEqual(field.props.name,name)) {
- return {
- ...field,
- props: {
- ...field.props,
- hidden: false
- }
- }
- }
- return field;
- }));
- validateContext.clear();
- },
- disable: (name: NamePath) => {
- if(fields.length==0){
- Toast.show('表单项未加载');
- return;
- }
- setFields(prevFields => prevFields.map((field) => {
- if (namePathEqual(field.props.name,name)) {
- return {
- ...field,
- props: {
- ...field.props,
- disabled: true
- }
- }
- }
- return field;
- }));
- validateContext.clear();
- },
+export const FormContext = React.createContext(null);
- disableAll:()=>{
- if(fields.length==0){
- Toast.show('表单项未加载');
- return;
- }
- setFields(prevFields => prevFields.map((field) => {
- return {
- ...field,
- props: {
- ...field.props,
- disabled: true
- }
- }
- }));
- validateContext.clear();
- },
+const FormComponent: React.FC = (props) => {
- enable: (name: NamePath) => {
- if(fields.length==0){
- Toast.show('表单项未加载');
- return;
- }
- setFields(prevFields => prevFields.map((field) => {
- if (namePathEqual(field.props.name,name)) {
- return {
- ...field,
- props: {
- ...field.props,
- disabled: false
- }
- }
- }
- return field;
- }));
- validateContext.clear();
- },
-
- enableAll:()=>{
- if(fields.length==0){
- Toast.show('表单项未加载');
- return;
- }
- setFields(prevFields => prevFields.map((field) => {
- return {
- ...field,
- props: {
- ...field.props,
- disabled: false
- }
- }
- }));
- validateContext.clear();
- },
-
- remove: (name: NamePath) => {
- if(fields.length==0){
- Toast.show('表单项未加载');
- return;
- }
- setFields(prevFields => prevFields.filter((field) => !namePathEqual(field.props.name,name)));
- validateContext.clear();
- },
-
- create: (field: FormField, index?: number) => {
- if(fields.length==0){
- Toast.show('表单项未加载');
- return;
- }
- setFields(prevFields => {
- const filteredFields = prevFields.filter((item) => item.props.name !== field.props.name);
- if (index === undefined || index < 0) {
- return [...filteredFields, field];
- } else {
- const newFields = [...filteredFields];
- newFields.splice(index, 0, field);
- return newFields;
- }
- });
- validateContext.clear();
- },
-
- getFieldValue(name: NamePath): any {
- return form.getFieldValue(name);
- },
-
- getFieldsValue(): any {
- return form.getFieldsValue();
- },
-
- getFieldProps(name: NamePath): FormField | null {
- for (const field of fields) {
- if (namePathEqual(field.props.name,name)) {
- return field;
- }
- }
- return null;
- },
-
- reloadOptions:(name: NamePath) => {
- optionContext.notify(name);
- },
-
- reloadAllOptions:()=>{
- optionContext.notifyAll();
- },
-
- setFieldValue(name: NamePath, value: any): void {
- form.setFieldValue(name, value);
- reloadContext.notify(name);
- validateContext?.validateField(name, formAction);
- },
-
- setFieldsValue(values: any): void {
- form.setFieldsValue(values);
- reloadContext.notifyAll();
- },
-
- setFields(fields: FiledData[]): void {
- form.setFields(fields);
- },
-
- validate(): Promise {
- return validateContext.validate(formAction);
- }
- }
-
- const formContextProps = {
- formAction: formAction,
- validateContext: validateContext,
- reloadContext:reloadContext,
- optionContext:optionContext
- }
+ const formInstance = props.form? props.form : new FormInstance();
const [fields, setFields] = React.useState([]);
+ formInstance.setFieldsUpdateDispatch(setFields);
+
+ const formControl = formInstance.getFormControlInstance() ;
const reloadFields = ()=>{
if(props.loadFields){
props.loadFields().then(fields=>{
setFields(fields);
+ formInstance.resetFields(fields);
})
}
}
@@ -356,16 +99,12 @@ const Form: React.FC = (props) => {
}, [props.loadFields]);
- React.useImperativeHandle(props.actionRef, () => {
- return formAction
- }, [fields]);
-
return (
{
props.onFinish && props.onFinish(values);
}}
@@ -384,4 +123,15 @@ const Form: React.FC = (props) => {
)
}
+type FormType = typeof FormComponent;
+type FormComponentType = FormType & {
+ useForm: ()=>FormInstance;
+};
+
+const Form = FormComponent as FormComponentType;
+Form.useForm = ()=>{
+ return new FormInstance();
+};
+
export default Form;
+
diff --git a/mobile-ui/src/pages/levave/form.tsx b/mobile-ui/src/pages/levave/form.tsx
index 47e5dabf..2041657e 100644
--- a/mobile-ui/src/pages/levave/form.tsx
+++ b/mobile-ui/src/pages/levave/form.tsx
@@ -6,7 +6,7 @@ import {FlowViewReactContext} from "@/components/flow/view";
import {fields} from "@/pages/levave/fields";
const LeaveForm: React.FC = (props) => {
- const formAction = props.formAction;
+ const formInstance = props.form;
const flowViewReactContext = useContext(FlowViewReactContext);
console.log('LeaveForm init:', props);
@@ -22,7 +22,7 @@ const LeaveForm: React.FC = (props) => {
useEffect(() => {
// 设置表单数据
if (props.dataVersion && props.data) {
- formAction.current?.setFieldsValue({
+ formInstance?.setFieldsValue({
...props.data
});
}
@@ -32,7 +32,7 @@ const LeaveForm: React.FC = (props) => {
return (