diff --git a/packages/amis-core/src/renderers/wrapControl.tsx b/packages/amis-core/src/renderers/wrapControl.tsx index b7f1f5fa8..f0bf2c075 100644 --- a/packages/amis-core/src/renderers/wrapControl.tsx +++ b/packages/amis-core/src/renderers/wrapControl.tsx @@ -223,14 +223,18 @@ export function wrapControl< if (propValue !== undefined && propValue !== null) { // 同步 value: 优先使用 props 中的 value - model.changeTmpValue(propValue); + model.changeTmpValue(propValue, 'controlled'); } else { // 备注: 此处的 value 是 schema 中的 value(和props.defaultValue相同) - const curTmpValue = isExpression(value) + const isExp = isExpression(value); + const curTmpValue = isExp ? FormulaExec['formula'](value, data) // 对组件默认值进行运算 : store?.getValueByName(model.name) ?? replaceExpression(value); // 优先使用公式表达式 // 同步 value - model.changeTmpValue(curTmpValue); + model.changeTmpValue( + curTmpValue, + isExp ? 'formulaChanged' : 'defaultValue' + ); } if ( @@ -354,7 +358,7 @@ export function wrapControl< !isEqual(props.value, model.tmpValue) ) { // 外部直接传入的 value 无需执行运算器 - model.changeTmpValue(props.value); + model.changeTmpValue(props.value, 'controlled'); } } else if ( model && @@ -386,46 +390,39 @@ export function wrapControl< !isEqual(curResult, model.tmpValue) ) { // 识别上下文变动、自身数值变动、公式运算结果变动 - model.changeTmpValue(curResult); + model.changeTmpValue(curResult, 'formulaChanged'); props.onChange?.(curResult, model.name, false); } else if (nowFormulaChecked) { const nowData = props.data[model.name]; // now 表达式,计算后的值永远相同 - model.changeTmpValue(nowData); + model.changeTmpValue(nowData, 'formulaChanged'); props.onChange?.(nowData, model.name, false); } } } else if (model) { const valueByName = getVariable(props.data, model.name); - if (isEqual(props.defaultValue, prevProps.defaultValue)) { - // value 非公式表达式时,name 值优先,若 defaultValue 主动变动时,则使用 defaultValue - if ( - // 然后才是查看关联的 name 属性值是否变化 - props.data !== prevProps.data && - (!model.emitedValue || - isEqual(model.emitedValue, model.tmpValue)) - ) { - model.changeEmitedValue(undefined); - const prevValueByName = getVariable(props.data, model.name); - if ( - (!isEqual(valueByName, prevValueByName) || - getVariable(props.data, model.name, false) !== - getVariable(prevProps.data, model.name, false)) && - !isEqual(valueByName, model.tmpValue) - ) { - model.changeTmpValue(valueByName); - } - } - } else if ( - !isEqual(props.defaultValue, prevProps.defaultValue) && - !isEqual(props.defaultValue, model.tmpValue) + // value 非公式表达式时,name 值优先,若 defaultValue 主动变动时,则使用 defaultValue + if ( + // 然后才是查看关联的 name 属性值是否变化 + props.data !== prevProps.data && + (!model.emitedValue || + isEqual(model.emitedValue, model.tmpValue)) ) { - // 组件默认值非公式 - const curValue = replaceExpression(props.defaultValue); - model.changeTmpValue(curValue); - if (props.onChange) { - props.onChange(curValue, model.name, false); + model.changeEmitedValue(undefined); + const prevValueByName = getVariable(props.data, model.name); + if ( + (!isEqual(valueByName, prevValueByName) || + getVariable(props.data, model.name, false) !== + getVariable(prevProps.data, model.name, false)) && + !isEqual(valueByName, model.tmpValue) + ) { + model.changeTmpValue( + valueByName, + props.formInited && !prevProps.formInited + ? 'formInited' + : 'dataChanged' + ); } } } @@ -589,7 +586,7 @@ export function wrapControl< value = pipeOut(value, oldValue, data); } - this.model.changeTmpValue(value); + this.model.changeTmpValue(value, 'input'); if (changeImmediately || conrolChangeImmediately || !formInited) { this.emitChange(submitOnChange); @@ -762,6 +759,7 @@ export function wrapControl< ref: this.controlRef, data: data || store?.data, value, + changeMotivation: model?.changeMotivation, defaultValue: control.value, formItemValue: value, // 为了兼容老版本的自定义组件 onChange: this.handleChange, diff --git a/packages/amis-core/src/store/formItem.ts b/packages/amis-core/src/store/formItem.ts index aaed67490..a6006ed19 100644 --- a/packages/amis-core/src/store/formItem.ts +++ b/packages/amis-core/src/store/formItem.ts @@ -61,6 +61,7 @@ export const FormItemStore = StoreNode.named('FormItemStore') isValueSchemaExp: types.optional(types.boolean, false), tmpValue: types.frozen(), emitedValue: types.frozen(), + changeMotivation: 'input', rules: types.optional(types.frozen(), {}), messages: types.optional(types.frozen(), {}), errorData: types.optional(types.array(ErrorDetail), []), @@ -1232,8 +1233,20 @@ export const FormItemStore = StoreNode.named('FormItemStore') } } - function changeTmpValue(value: any) { + function changeTmpValue( + value: any, + changeReason?: + | 'formInited' // 表单初始化 + | 'dataChanged' // 表单数据变化 + | 'formulaChanged' // 公式运算结果变化 + | 'controlled' // 受控 + | 'input' // 用户交互改变 + | 'defaultValue' // 默认值 + ) { self.tmpValue = value; + if (changeReason) { + self.changeMotivation = changeReason; + } } function changeEmitedValue(value: any) { diff --git a/packages/amis-editor/src/plugin/Form/Item.tsx b/packages/amis-editor/src/plugin/Form/Item.tsx index a190e453a..1d66a5ea8 100644 --- a/packages/amis-editor/src/plugin/Form/Item.tsx +++ b/packages/amis-editor/src/plugin/Form/Item.tsx @@ -208,7 +208,7 @@ export class ItemPlugin extends BasePlugin { const context = event.context; if ( - /\$/.test(context.info.renderer.name!) && + context.info.renderer.isFormItem && context.diff?.some(change => change.path?.join('.') === 'value') ) { const change: any = find( diff --git a/packages/amis/src/renderers/Form/InputFile.tsx b/packages/amis/src/renderers/Form/InputFile.tsx index 22861ca89..aeff66a56 100644 --- a/packages/amis/src/renderers/Form/InputFile.tsx +++ b/packages/amis/src/renderers/Form/InputFile.tsx @@ -348,7 +348,7 @@ export default class FileControl extends React.Component { file: any; executor: () => void; }> = []; - initAutoFill: boolean; + initedFilled = false; static valueToFile( value: string | FileValue, @@ -398,7 +398,6 @@ export default class FileControl extends React.Component { const joinValues = props.joinValues; const delimiter = props.delimiter as string; let files: Array = []; - this.initAutoFill = !!props.initAutoFill; if (value && value instanceof Blob) { files = [value as any]; @@ -438,11 +437,16 @@ export default class FileControl extends React.Component { } componentDidMount() { - if (this.initAutoFill) { - const {formInited, addHook} = this.props; - formInited || !addHook - ? this.syncAutoFill() - : addHook(this.syncAutoFill, 'init'); + const {formInited, addHook} = this.props; + + if (formInited || !addHook) { + this.initedFilled = true; + this.props.initAutoFill && this.syncAutoFill(); + } else if (addHook) { + addHook(() => { + this.initedFilled = true; + this.props.initAutoFill && this.syncAutoFill(); + }, 'init'); } } @@ -494,7 +498,9 @@ export default class FileControl extends React.Component { { files: files }, - props.formInited !== false ? this.syncAutoFill : undefined + props.changeMotivation !== 'formInited' && this.initedFilled + ? this.syncAutoFill + : undefined ); } } @@ -1486,7 +1492,8 @@ export default class FileControl extends React.Component { {files.map((file, index) => { const filename = file[nameField as keyof typeof file] || - (file as FileValue).filename; + (file as FileValue).filename || + file.name; return (
  • diff --git a/packages/amis/src/renderers/Form/InputImage.tsx b/packages/amis/src/renderers/Form/InputImage.tsx index 9558a0b57..dbde438a5 100644 --- a/packages/amis/src/renderers/Form/InputImage.tsx +++ b/packages/amis/src/renderers/Form/InputImage.tsx @@ -373,7 +373,8 @@ export default class ImageControl extends React.Component< delimiter: ',', autoUpload: true, multiple: false, - dropCrop: true + dropCrop: true, + initAutoFill: true }; static valueToFile( @@ -428,7 +429,7 @@ export default class ImageControl extends React.Component< resolve?: (value?: any) => void; emitValue: any; unmounted = false; - initAutoFill: boolean; + initedFilled = false; // 文件重新上传的位置标记,用以定位替换 reuploadIndex: undefined | number = undefined; @@ -439,7 +440,6 @@ export default class ImageControl extends React.Component< const joinValues = props.joinValues; const delimiter = props.delimiter as string; let files: Array = []; - this.initAutoFill = !!(props.initAutoFill ?? true); if (value) { // files = (multiple && Array.isArray(value) ? value : joinValues ? (value as string).split(delimiter) : [value]) @@ -488,11 +488,16 @@ export default class ImageControl extends React.Component< } componentDidMount() { - if (this.initAutoFill) { - const {formInited, addHook} = this.props; - formInited || !addHook - ? this.syncAutoFill() - : addHook(this.syncAutoFill, 'init'); + const {formInited, addHook} = this.props; + + if (formInited || !addHook) { + this.initedFilled = true; + this.props.initAutoFill && this.syncAutoFill(); + } else if (addHook) { + addHook(() => { + this.initedFilled = true; + this.props.initAutoFill && this.syncAutoFill(); + }, 'init'); } if (this.props.initCrop && this.files.length) { @@ -545,7 +550,9 @@ export default class ImageControl extends React.Component< { files: (this.files = files) }, - this.initAutoFill ? this.syncAutoFill : () => {} + props.changeMotivation !== 'formInited' && this.initedFilled + ? this.syncAutoFill + : undefined ); } @@ -1380,7 +1387,7 @@ export default class ImageControl extends React.Component< }, () => { if (!needUploading) { - this.onChange(false, true, this.initAutoFill); + this.onChange(false, true, this.props.initAutoFill); } } );