Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangfisher committed Aug 25, 2024
1 parent 5a9584a commit b223673
Show file tree
Hide file tree
Showing 16 changed files with 232 additions and 111 deletions.
16 changes: 6 additions & 10 deletions example/src/forms/network/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const Network = createForm({
},
interface: {
value: "wifi",
title: 1,//"网卡类型",
title: "网卡类型",
enable: true,
select: () => {
return [
Expand Down Expand Up @@ -103,7 +103,7 @@ const Network = createForm({
submit: { // 这是一个动作,
title: "提交wifi",
enable: (net: any) => net.interface.value === "wifi",
validate: (value: string) => value.length > 6,
// validate: (value: string) => value.length > 6,
execute:async (wifi:any)=>{
await delay(2000)
console.log("提交wifi=",wifi)
Expand Down Expand Up @@ -145,9 +145,6 @@ const Network = createForm({
},
progressSubmit2: { // 这是一个动作,
title: "提交进度2",
validate:async ()=>{
return true
},
execute:action(async (fields,{getProgressbar})=>{
console.log("submit fields=",fields)
const bar = getProgressbar()
Expand Down Expand Up @@ -214,24 +211,23 @@ const Network = createForm({
},
timeoutSubmit: {
title: "提交超时倒计时",
execute: computed(async () => {
execute: computed(async (scope: Dict) => {
console.log("ping=",Object.assign({},scope));
await delay(5000);
},[],{timeout:2000}),
},
ping: {
title: "测试网络连通性",
scope:"fields.wifi",
enable: computed((wifi: any) => {
return wifi.ssid.value.length > 0
},{scope:"fields.wifi"}),
execute: async (a: Dict) => {
await delay(2000);
console.log(a);
await delay(1000);
console.log("ping=",Object.assign({},a));
},
},
// 向导表单:上一步
previous:{

enable: (wifi: any) => wifi.ssid.value.length > 0,
execute:async ()=>{
return 1
Expand Down
5 changes: 5 additions & 0 deletions example/src/forms/network/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ const NetworkForm = ()=>{
return <Button loading={loading} timeout={timeout} visible={visible} enable={enable} onClick={run()}>{title}</Button>
}}
</Network.Action>
<Network.Action<typeof Network.fields.wifi.submit> name="fields.wifi.standardSubmit" >
{({title,visible,loading,enable,run,timeout})=>{
return <Button loading={loading} timeout={timeout} visible={visible} enable={enable} onClick={run()}>{title}</Button>
}}
</Network.Action>
<Network.Action<typeof Network.fields.wifi.timeoutSubmit> name="fields.wifi.timeoutSubmit" >
{({title,visible,loading,enable,run,error,timeout})=>{
return <Button loading={loading} timeout={timeout} visible={visible} enable={enable} error={error} onClick={run()}>{title}</Button>
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import { ReactNode, useCallback, useRef, RefObject,useState} from "react";
import React from "react";
import type { FormDefine, FormStore } from "./form";
import { AsyncComputedDefine, AsyncComputedGetter, AsyncComputedObject, ComputedDescriptorDefine, ComputedOptions, ComputedParams, Dict, RuntimeComputedOptions, computed, getValueByPath} from '@speedform/reactive';
import { AsyncComputedDefine, AsyncComputedGetter, AsyncComputedObject, ComputedDescriptorDefine, ComputedOptions, ComputedParams, Dict, RuntimeComputedOptions, computed, getVal, getValueByPath} from '@speedform/reactive';
import { omit } from "flex-tools/object/omit";
import { getFormData } from "./serialize";
import { getId } from "./utils";
Expand Down Expand Up @@ -357,6 +357,8 @@ export function createUseAction<State extends FormDefine = FormDefine>(store:For
execute:action(executor,options)
}
})
// 读取一次以触发计算属性对象的创建
getValueByPath(state,['actions',actionName])
}
ref.current = actionName
}
Expand Down
196 changes: 133 additions & 63 deletions packages/core/src/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import { createFieldComponent, FormFields } from './field';
import { createFieldGroupComponent } from "./fieldGroup";
import { assignObject } from "flex-tools/object/assignObject";
import { FormActions, UseActionType, createActionComponent, createUseAction, getAction } from './action';
import { FIELDS_STATE_KEY, VALIDATE_COMPUTED_GROUP } from './consts';
import { ACTIONS_STATE_KEY, FIELDS_STATE_KEY, VALIDATE_COMPUTED_GROUP } from './consts';
import { defaultObject } from "flex-tools/object/defaultObject";
import { createObjectProxy } from "./utils";
import { createLoadApi, createGetValuesApi } from "./serialize";
Expand Down Expand Up @@ -167,6 +167,21 @@ export type FormStatus = 'idle'
// actions : Actions // 表单动作
// }

/**
* 设置表单默认属性
* title?:ComputedAttr<string> // 动作标题
help?:ComputedAttr<string> // 动作帮助
tips?:ComputedAttr<string> // 动作提示
visible?:ComputedAttr<boolean> // 是否可见
enable?:ComputedAttr<boolean> // 是否可用
valid?:ComputedAttr<boolean> // 是否有效
readonly?:ComputedAttr<boolean> // 是否只读
* @param define
*/
function setFormDefault<T extends Dict>(define:T){
return defaultObject(define,defaultFormProps) as typeof defaultFormProps & T
}


/**
*
Expand Down Expand Up @@ -202,21 +217,6 @@ function createValidatorHook(valuePath:string[],options:ComputedOptions){
}


/**
* 设置表单默认属性
* title?:ComputedAttr<string> // 动作标题
help?:ComputedAttr<string> // 动作帮助
tips?:ComputedAttr<string> // 动作提示
visible?:ComputedAttr<boolean> // 是否可见
enable?:ComputedAttr<boolean> // 是否可用
valid?:ComputedAttr<boolean> // 是否有效
readonly?:ComputedAttr<boolean> // 是否只读
* @param define
*/
function setFormDefault<T extends Dict>(define:T){
return defaultObject(define,defaultFormProps) as typeof defaultFormProps & T
}



/**
Expand All @@ -238,25 +238,29 @@ function setFormDefault<T extends Dict>(define:T){
*
*/
function createActionHook(valuePath:string[],options:ComputedOptions){
if(valuePath.length>1 && valuePath[valuePath.length-1]=='execute'){
// 默认不自动执行,需要手动调用action.execute.run()来执行
options.immediate = false
// 如果没有指定scope,则默认指向fields,这样就可以直接使用fields下的字段,而不需要fields前缀
if(options.scope){
if(Array.isArray(options.scope)){
// 如果scope中没有fields,则添加fields,并且保证fields在第一个位置
if(options.scope.length>0 && options.scope[0]!=FIELDS_STATE_KEY){
options.scope.unshift(FIELDS_STATE_KEY)
}
if(valuePath.length>1 ){
if(valuePath[valuePath.length-1]=='execute'){
// 默认不自动执行,需要手动调用action.execute.run()来执行
options.immediate = false
// 如果没有指定scope,则默认指向fields,这样就可以直接使用fields下的字段,而不需要fields前缀
if(options.scope){
if(Array.isArray(options.scope)){
// 如果scope中没有fields,则添加fields,并且保证fields在第一个位置
if(options.scope.length>0 && options.scope[0]!=FIELDS_STATE_KEY){
options.scope.unshift(FIELDS_STATE_KEY)
}
}
}else{// 如果没有指定scope,则默认指向fields
options.scope = [FIELDS_STATE_KEY]
}
}else{// 如果没有指定scope,则默认指向fields,
options.scope = [FIELDS_STATE_KEY]
}
options.noReentry = true // 禁止重入
options.noReentry = true // 禁止重入
}
}

}
/**
* 对所有位于fields下的的依赖均自动添加fields前缀,这样在声明依赖时就可以省略fields前缀
*
* @param valuePath
* @param getter
* @param options
Expand All @@ -273,6 +277,7 @@ function createDepsHook(valuePath:string[],options:ComputedOptions){
}
}


/**
*
* 冻结表单,即表单计算函数不再执行
Expand All @@ -292,6 +297,27 @@ export function getFieldName(valuePath:string[]){
valuePath.slice(0,-1).join(".") : valuePath.join(".") : ''
}

export type FormObject<State extends FormDefine=FormDefine> = {
state: FormState<State['fields']>,
useState: (fn: (state: FormState<State['fields']>) => any) => any,
setState: (fn: (draft: FormState<State['fields']>) => void) => void,
Form: FormComponent<State['fields']>,
Field: ReactFC<any>,
Group: ReactFC<any>,
Action: ReactFC<any>,
Submit: ReactFC<any>,
Reset: ReactFC<any>,
useAction: UseActionType,
fields: State['fields'],
actions: State['actions'],
getAction: (name: string) => any,
freeze: (value?: boolean) => void,
load: (data: Dict) => void,
getValues: () => Dict,
computedObjects: Dict,
watchObjects: Dict,
validate: (value?: Dict) => Promise<boolean>
}
/**
* 创建声明表单
*
Expand All @@ -310,7 +336,8 @@ export function createForm<State extends FormDefine=FormDefine>(schema: State,op

// 创建表单Store对象实例
const store = createStore(schema ,{
debug:opts.debug,
debug:opts.debug,
immediate:true, // 默认立即执行完成所有计算属性的初始化
// 计算函数作用域默认指向fields
scope: ()=>[FIELDS_STATE_KEY],
// 创建计算函数时的钩子函数,可以在创建前做一些不可描述的处理
Expand All @@ -322,15 +349,13 @@ export function createForm<State extends FormDefine=FormDefine>(schema: State,op
// 3. 将表单actions的execute的onComputedResult指向其current
createActionHook(valuePath,options)
},
onComputedDraft(draft,{computedType,valuePath}){
// 针对计算属性
getRootScope(draft,{computedType,valuePath}){
// 修改fields下的所有计算函数的作用域根,使之总是指向fields开头
// 这样可以保证在计算函数中,当scope->Root时,总是指向fields,否则就需要state.fields.xxx.xxx
if(computedType==='Computed' && valuePath.length >0 && valuePath[0]==FIELDS_STATE_KEY){
return draft.fields
}
},
immediate:true // 默认立即执行完成所有计算属性的初始化
}
});

/**
Expand Down Expand Up @@ -370,7 +395,7 @@ export function createForm<State extends FormDefine=FormDefine>(schema: State,op
computedObjects: formStore.computedObjects,
watchObjects : formStore.watchObjects,
validate : createValidator<State>(formStore)
};
}
}


Expand All @@ -379,55 +404,100 @@ export function createForm<State extends FormDefine=FormDefine>(schema: State,op
*
* 当使用标准的表单提交模式时,使用该组件
*
* 简单用法:
* <Network.Form
* scope="wifi"
* action="/api/wifi"
* method="post"
* enctype="application/x-www-form-urlencoded"
* onSubmit={(data)=>{
* console.log(data)
* }}
* onLoading={(loading)=>{ }}
* onError={(error)=>{}}
* onValid={(valid)=>{}}
* >
* <Network.Field name="username"></Network.Field>
* <Network.Field name="password"></Network.Field>
* <Network.Submit>提交</Network.Submit>
* </Network.Form>
*
* <Form></From> // 表单组件
* <Network.Form<typeof Network.wifi> name="wifi">
* <Network.Field name="ssid"></Network.Field> // 声明字段
* <Network.Submit>
* {({action})=>{
* retrun <button onClick={action()}></button>
* }}
* </Network.Submit>
* </Network.Form> // 声明子表单
*
* 高级用法:
* <Network.Form
* scope="wifi" 提交范围,默认是整个表单fields
* format="json" 提交数据
*
* >
* {({timeout,loading})=>{
* return <>
* <Network.Field name="username"></Network.Field>
* <Network.Field name="password"></Network.Field>
* <Network.Submit>
* </>
* }}
* </Network.Form>
*
*
* @param this
* @param store
* @returns
*/
function createFormComponent<State extends Dict>(store: FormStore<State>,formOptions:RequiredFormOptions<State>): FormComponent<State['fields']> {

function createFormComponent<State extends FormDefine>(store: FormStore<State>,formOptions:RequiredFormOptions<State>): FormComponent<State['fields']> {
const Action = createActionComponent<State>(store)
const useAction = createUseAction<State>(store)

return React.forwardRef<HTMLFormElement>((props: FormProps<State['fields']>,ref:React.ForwardedRef<HTMLFormElement>) => {
const {children } = props;


//
const { run } = useAction(async (scope:any,params)=>{
await store.computedObjects.runGroup(VALIDATE_COMPUTED_GROUP)

},{
save:false
})

// 提交表单
const onSubmit = useCallback(async (ev: React.FormEvent<HTMLFormElement>) => {
const onSubmit = useCallback(async (ev: React.FormEvent<HTMLFormElement>) => {
run()
// 提交前运行校验
if(formOptions.validAt==='submit'){
await store.computedObjects.runGroup(VALIDATE_COMPUTED_GROUP)
}
debugger
}
await store.computedObjects.runGroup(VALIDATE_COMPUTED_GROUP)
ev.preventDefault();
if(store.state.validate){
return true
}else{
return false
}
},[]);

// 重置表单
const onReset = useCallback((e: React.FormEvent<HTMLFormElement>) => {

},[]);

return (
<form ref={ref}
className="speedform"
{...props}
onSubmit={onSubmit}
onReset={onReset}
>{children}</form>
<>
<form ref={ref} className="speedform" {...props} onSubmit={onSubmit} onReset={onReset}>
{children}
</form>
</>
// <Action<any> name="$submit">
// {(params)=>{

// return <>{
// typeof(children)=='function' ?
// (children as any)(params)
// :
// <form ref={ref}
// className="speedform"
// {...props}
// onSubmit={(ev:any)=>onSubmit(ev,run)}
// onReset={onReset}
// >{children}</form>
// }
// </>
// }}

// </Action>

)
}) as FormComponent<State['fields']>
}
Expand Down
Loading

0 comments on commit b223673

Please sign in to comment.