diff --git a/packages/core/client/src/flow/models/data-blocks/form/QuickEditForm.tsx b/packages/core/client/src/flow/models/data-blocks/form/QuickEditForm.tsx index a32a7d8889..b4e619ef55 100644 --- a/packages/core/client/src/flow/models/data-blocks/form/QuickEditForm.tsx +++ b/packages/core/client/src/flow/models/data-blocks/form/QuickEditForm.tsx @@ -23,27 +23,28 @@ import { import { Button, InputRef, Skeleton } from 'antd'; import _ from 'lodash'; import React, { createRef } from 'react'; +import { SkeletonFallback } from '../../../components/SkeletonFallback'; import { DataBlockModel } from '../../base/BlockModel'; export class QuickEditForm extends DataBlockModel { form: Form; fieldPath: string; + declare resource: SingleRecordResource; declare collection: Collection; static async open(options: { target: any; - flowEngine: FlowEngine; dataSourceKey: string; collectionName: string; fieldPath: string; filterByTk: string; }) { - const { flowEngine, dataSourceKey, collectionName, fieldPath, target, filterByTk } = options; - const model = flowEngine.createModel({ + const { target, dataSourceKey, collectionName, fieldPath, filterByTk } = options; + const model = this.flowEngine.createModel({ use: 'QuickEditForm', stepParams: { - initial: { + propsFlow: { step1: { dataSourceKey, collectionName, @@ -52,81 +53,75 @@ export class QuickEditForm extends DataBlockModel { }, }, }) as QuickEditForm; - await model.open({ target, filterByTk }); - options.flowEngine.removeModel(model.uid); + + await this.flowEngine.context.popover.open({ + target, + placement: 'rightTop', + content: (popover) => { + return ( + } + model={model} + sharedContext={{ currentView: popover }} + extraContext={{ filterByTk }} + /> + ); + }, + }); } - async open({ target, filterByTk }: { target: any; filterByTk: string }) { - await this.applyFlow('initial', { filterByTk }); - return new Promise((resolve, reject) => { - const inputRef = createRef(); - const popover = this.ctx.globals.popover.open({ - target, - content: ( -
{ - e.preventDefault(); - await this.form.submit(); - await this.resource.save( - { - [this.fieldPath]: this.form.values[this.fieldPath], - }, - { refresh: false }, - ); - popover.destroy(); - resolve(this.form.values); - }} - > - - - - {this.mapSubModels('fields', (field) => { - return ( - } - /> - ); - })} - - - - - - - -
- ), - onRendered: () => { - setTimeout(() => { - // 聚焦 Popover 内第一个 input 或 textarea - const el = document.querySelector( - '.quick-edit-form input, .quick-edit-form textarea, .quick-edit-form select', - ) as HTMLInputElement | HTMLTextAreaElement | null; - el?.focus(); - }, 200); - // inputRef.current.focus(); - }, - placement: 'rightTop', - }); - }); + render() { + return ( +
{ + e.preventDefault(); + await this.form.submit(); + await this.resource.save( + { + [this.fieldPath]: this.form.values[this.fieldPath], + }, + { refresh: false }, + ); + this.ctx.shared.currentView.close(); + }} + > + + + + {this.mapSubModels('fields', (field) => { + return ( + } + /> + ); + })} + + + + + + + +
+ ); } } QuickEditForm.registerFlow({ - key: 'initial', + key: 'propsFlow', + auto: true, steps: { step1: { async handler(ctx, params) { diff --git a/packages/core/client/src/flow/models/data-blocks/table/TableModel.tsx b/packages/core/client/src/flow/models/data-blocks/table/TableModel.tsx index 49c965e706..68639826d1 100644 --- a/packages/core/client/src/flow/models/data-blocks/table/TableModel.tsx +++ b/packages/core/client/src/flow/models/data-blocks/table/TableModel.tsx @@ -164,7 +164,6 @@ export class TableModel extends DataBlockModel { try { await QuickEditForm.open({ target: ref.current, - flowEngine: this.flowEngine, dataSourceKey: this.collection.dataSourceKey, collectionName: this.collection.name, fieldPath: dataIndex, diff --git a/packages/core/flow-engine/src/flowEngine.ts b/packages/core/flow-engine/src/flowEngine.ts index aabc73534b..a12ad3722c 100644 --- a/packages/core/flow-engine/src/flowEngine.ts +++ b/packages/core/flow-engine/src/flowEngine.ts @@ -226,6 +226,7 @@ export class FlowEngine { if (this.modelClasses.has(name)) { console.warn(`FlowEngine: Model class with name '${name}' is already registered and will be overwritten.`); } + (modelClass as typeof FlowModel).flowEngine = this; // 绑定 FlowEngine 实例到 Model 类 this.modelClasses.set(name, modelClass); } @@ -316,7 +317,7 @@ export class FlowEngine { if (uid && this.modelInstances.has(uid)) { return this.modelInstances.get(uid) as T; } - const modelInstance = new (ModelClass as ModelConstructor)({ ...options, flowEngine: this } as any); + const modelInstance = new (ModelClass as ModelConstructor)({ ...options } as any); modelInstance.onInit(options); diff --git a/packages/core/flow-engine/src/models/flowModel.tsx b/packages/core/flow-engine/src/models/flowModel.tsx index d6be7878a3..9755356a2d 100644 --- a/packages/core/flow-engine/src/models/flowModel.tsx +++ b/packages/core/flow-engine/src/models/flowModel.tsx @@ -39,11 +39,13 @@ const modelMetas = new WeakMap(); const modelFlows = new WeakMap>(); export class FlowModel { + public static flowEngine: FlowEngine; + public readonly uid: string; public sortIndex: number; public props: IModelComponentProps = {}; public stepParams: StepParams = {}; - public flowEngine: FlowEngine; + // public flowEngine: FlowEngine; public parent: ParentFlowModel; public subModels: Structure['subModels']; private _options: FlowModelOptions; @@ -72,9 +74,12 @@ export class FlowModel { private observerDispose: () => void; constructor(options: FlowModelOptions) { - if (options?.flowEngine?.getModel(options.uid)) { + if (!this.flowEngine) { + throw new Error('FlowModel must be initialized with a FlowEngine instance.'); + } + if (this.flowEngine.getModel(options.uid)) { // 此时 new FlowModel 并不创建新实例,而是返回已存在的实例,避免重复创建同一个model实例 - return options.flowEngine.getModel(options.uid); + return this.flowEngine.getModel(options.uid); } if (!options.uid) { @@ -85,7 +90,6 @@ export class FlowModel { this.props = options.props || {}; this.stepParams = options.stepParams || {}; this.subModels = {}; - this.flowEngine = options.flowEngine; this.sortIndex = options.sortIndex || 0; this._options = options; @@ -132,6 +136,11 @@ export class FlowModel { return modelMetas.get(this); } + get flowEngine() { + // 取静态属性 flowEngine + return (this.constructor as typeof FlowModel).flowEngine; + } + private createSubModels(subModels: Record) { Object.entries(subModels || {}).forEach(([key, value]) => { if (Array.isArray(value)) { @@ -162,7 +171,7 @@ export class FlowModel { * @param {FlowEngine} flowEngine FlowEngine实例 */ setFlowEngine(flowEngine: FlowEngine): void { - this.flowEngine = flowEngine; + // this.flowEngine = flowEngine; } static define(meta: FlowModelMeta) { diff --git a/packages/core/flow-engine/src/views/useDrawer.tsx b/packages/core/flow-engine/src/views/useDrawer.tsx index 36271a7754..ac1320ee7e 100644 --- a/packages/core/flow-engine/src/views/useDrawer.tsx +++ b/packages/core/flow-engine/src/views/useDrawer.tsx @@ -40,8 +40,6 @@ export function useDrawer() { // 支持 content 为函数,传递 currentDrawer const content = typeof config.content === 'function' ? config.content(currentDrawer) : config.content; - console.log('useDrawer open', config, content); - const drawer = ( (({ afterClose, content, placement, rect, ...props }, ref) => { + const [visible, setVisible] = React.useState(true); + const [config, setConfig] = React.useState({ content, placement, rect, ...props }); + + React.useImperativeHandle(ref, () => ({ + destroy: () => setVisible(false), + update: (newConfig: any) => setConfig((prev) => ({ ...prev, ...newConfig })), + close: (result?: any) => setVisible(false), + })); + + // 关闭后触发 afterClose + React.useEffect(() => { + if (!visible) { + afterClose?.(); + } + }, [visible, afterClose]); + + return ( + document.body} + onOpenChange={(nextOpen) => { + setVisible(nextOpen); + if (!nextOpen) { + afterClose?.(); + } + }} + {...config} + > + + + ); +}); + export function usePopover() { - const holderRef = React.useRef(null); + const holderRef = React.useRef(null); const open = (config) => { uuid += 1; - const { target, placement = 'rightTop', content, onRendered, ...rest } = config; - const popoverRef = React.createRef<{ destroy: () => void; update: (config: any) => void }>(); + const { target, placement = 'rightTop', content, ...rest } = config; + const popoverRef = React.createRef(); // 计算目标位置 const rect = target?.getBoundingClientRect?.() ?? { top: 0, left: 0 }; // eslint-disable-next-line prefer-const let closeFunc: (() => void) | undefined; - const PopoverComponent = (props) => { - const [open, setOpen] = React.useState(true); - React.useEffect(() => { - onRendered?.(); - }, []); - return ( - setOpen(open)} - content={content} - placement={placement} - getPopupContainer={() => document.body} - {...rest} - > - - - ); - }; + let resolvePromise: (value?: any) => void; + const promise = new Promise((resolve) => { + resolvePromise = resolve; + }); - const popover = ; - // eslint-disable-next-line prefer-const + // 构造 currentPopover 实例 + const currentPopover = { + destroy: () => popoverRef.current?.destroy(), + update: (newConfig) => popoverRef.current?.update(newConfig), + close: (result?: any) => { + resolvePromise?.(result); + popoverRef.current?.close(result); + }, + }; + const children = typeof content === 'function' ? content(currentPopover) : content; + + const popover = ( + { + closeFunc?.(); + config.onClose?.(); + resolvePromise?.(config.result); + }} + {...rest} + /> + ); closeFunc = holderRef.current?.patchElement(popover); - return { - destroy: () => closeFunc?.(), - // update: (newConfig) => ... // 可选:实现更新逻辑 - }; + return Object.assign(promise, currentPopover); }; const api = React.useMemo(() => ({ open }), []);