mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
fix: 修复弹窗中的弹窗编辑器中出现重复的问题 (#10073)
This commit is contained in:
parent
8563dd04ea
commit
c9073e9716
@ -40,6 +40,7 @@ export interface EditorProps extends PluginEventListener {
|
||||
superEditorData?: any;
|
||||
withSuperDataSchema?: boolean;
|
||||
/** 当前 Editor 为 SubEditor 时触发的宿主节点 */
|
||||
hostManager?: EditorManager;
|
||||
hostNode?: EditorNodeType;
|
||||
dataBindingChange?: (
|
||||
value: string,
|
||||
@ -154,6 +155,7 @@ export default class Editor extends Component<EditorProps> {
|
||||
onChange,
|
||||
showCustomRenderersPanel,
|
||||
superEditorData,
|
||||
hostManager,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@ -179,7 +181,7 @@ export default class Editor extends Component<EditorProps> {
|
||||
this.store.setShowCustomRenderersPanel(showCustomRenderersPanel);
|
||||
}
|
||||
|
||||
this.manager = new EditorManager(config, this.store);
|
||||
this.manager = new EditorManager(config, this.store, hostManager);
|
||||
|
||||
// 子编辑器不再重新设置 editorStore
|
||||
if (!(props.isSubEditor && (window as any).editorStore)) {
|
||||
|
@ -4,17 +4,20 @@ import React from 'react';
|
||||
import {EditorStoreType} from '../../store/editor';
|
||||
import {JSONGetById, modalsToDefinitions, translateSchema} from '../../util';
|
||||
import {Button, Icon, ListMenu, PopOverContainer, confirm} from 'amis';
|
||||
import {EditorManager} from '../../manager';
|
||||
|
||||
export interface DialogListProps {
|
||||
classnames: ClassNamesFn;
|
||||
store: EditorStoreType;
|
||||
manager: EditorManager;
|
||||
}
|
||||
|
||||
export default observer(function DialogList({
|
||||
classnames: cx,
|
||||
store
|
||||
store,
|
||||
manager
|
||||
}: DialogListProps) {
|
||||
const modals = store.modals;
|
||||
const modals = store.modals.filter(item => !item.disabled);
|
||||
|
||||
const handleAddDialog = React.useCallback(() => {
|
||||
const modal = {
|
||||
@ -29,7 +32,7 @@ export default observer(function DialogList({
|
||||
]
|
||||
};
|
||||
|
||||
store.openSubEditor({
|
||||
manager.openSubEditor({
|
||||
title: '编辑弹窗',
|
||||
value: modal,
|
||||
onChange: ({definitions, ...modal}: any, diff: any) => {
|
||||
@ -42,19 +45,12 @@ export default observer(function DialogList({
|
||||
const index = parseInt(event.currentTarget.getAttribute('data-index')!, 10);
|
||||
const modal = store.modals[index];
|
||||
const modalId = modal.$$id!;
|
||||
store.openSubEditor({
|
||||
manager.openSubEditor({
|
||||
title: '编辑弹窗',
|
||||
value: {
|
||||
type: 'dialog',
|
||||
...(modal as any),
|
||||
definitions: modalsToDefinitions(
|
||||
store.modals.filter(
|
||||
(m: any) =>
|
||||
// 不要把自己下发,不允许弹窗自己再弹出自己
|
||||
// 不要下发自己内容里面内嵌的弹窗,否则会导致子弹窗里面的弹窗列表重复
|
||||
m.$$id !== modalId && !JSONGetById(modal, m.$$id)
|
||||
)
|
||||
)
|
||||
definitions: modalsToDefinitions(store.modals, {}, modal)
|
||||
},
|
||||
onChange: ({definitions, ...modal}: any, diff: any) => {
|
||||
store.updateModal(modalId, modal, definitions);
|
||||
|
@ -273,7 +273,7 @@ export class OutlinePanel extends React.Component<PanelProps> {
|
||||
|
||||
render() {
|
||||
const {curSearchElemKey} = this.state;
|
||||
const {store} = this.props;
|
||||
const {store, manager} = this.props;
|
||||
const outlineTabsKey = store.outlineTabsKey || 'component-outline';
|
||||
const options = store.outline;
|
||||
|
||||
@ -341,7 +341,7 @@ export class OutlinePanel extends React.Component<PanelProps> {
|
||||
eventKey={'dialog-outline'}
|
||||
title={'弹窗列表'}
|
||||
>
|
||||
<DialogList store={store} classnames={cx} />
|
||||
<DialogList manager={manager} store={store} classnames={cx} />
|
||||
</Tab>
|
||||
)}
|
||||
</Tabs>
|
||||
|
@ -146,6 +146,7 @@ export class SubEditor extends React.Component<SubEditorProps> {
|
||||
ref={store.subEditorRef}
|
||||
onChange={onChange}
|
||||
data={store.subEditorContext?.data}
|
||||
hostManager={manager}
|
||||
hostNode={store.subEditorContext?.hostNode}
|
||||
superEditorData={superEditorData}
|
||||
schemaFilter={manager.config.schemaFilter}
|
||||
|
@ -215,7 +215,8 @@ export class EditorManager {
|
||||
|
||||
constructor(
|
||||
readonly config: EditorManagerConfig,
|
||||
readonly store: EditorStoreType
|
||||
readonly store: EditorStoreType,
|
||||
readonly parent?: EditorManager
|
||||
) {
|
||||
// 传给 amis 渲染器的默认 env
|
||||
this.env = {
|
||||
@ -1358,6 +1359,20 @@ export class EditorManager {
|
||||
* @param config
|
||||
*/
|
||||
openSubEditor(config: SubEditorContext) {
|
||||
if (
|
||||
['dialog', 'drawer', 'confirmDialog'].includes(config.value.type) &&
|
||||
this.parent
|
||||
) {
|
||||
let parent: EditorManager | undefined = this.parent;
|
||||
while (parent) {
|
||||
if (parent.store.schema.$$id === config.value.$$id) {
|
||||
toast.warning('所选弹窗已经被打开,不能多次打开');
|
||||
return;
|
||||
}
|
||||
|
||||
parent = parent.parent;
|
||||
}
|
||||
}
|
||||
this.store.openSubEditor(config);
|
||||
}
|
||||
|
||||
@ -2212,6 +2227,7 @@ export class EditorManager {
|
||||
this.trigger('dispose', {
|
||||
data: this
|
||||
});
|
||||
delete (this as any).parent;
|
||||
this.toDispose.forEach(fn => fn());
|
||||
this.toDispose = [];
|
||||
this.plugins.forEach(p => p.dispose?.());
|
||||
|
@ -133,6 +133,10 @@ export type EditorModalBody = (DialogSchema | DrawerSchema) & {
|
||||
// 如果是公共弹窗,在 definitions 中的 key
|
||||
$$ref?: string;
|
||||
|
||||
// 内嵌弹窗会转成公共弹窗下发给子弹窗,否则子弹窗里面无法选择
|
||||
// 这类会在 definition 里面标记原始位置
|
||||
$$originId?: string;
|
||||
|
||||
// 弹出方式
|
||||
actionType?: string;
|
||||
};
|
||||
@ -548,10 +552,14 @@ export const MainStore = types
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const isSubEditor = self.isSubEditor;
|
||||
|
||||
return JSONPipeOut(
|
||||
JSONGetById(self.schema, self.activeId),
|
||||
getEnv(self).isHiddenProps ||
|
||||
((key, props) =>
|
||||
// 如果是子弹窗,不显示 definitions,要是通过代码模式改了,就麻烦了
|
||||
(isSubEditor && key === 'definitions') ||
|
||||
(key.substring(0, 2) === '$$' &&
|
||||
key !== '$$comments' &&
|
||||
key !== '$$commonSchema') ||
|
||||
@ -1015,15 +1023,7 @@ export const MainStore = types
|
||||
get modals(): Array<EditorModalBody> {
|
||||
const schema = self.schema;
|
||||
const modals: Array<DialogSchema | DrawerSchema> = [];
|
||||
Object.keys(schema.definitions || {}).forEach(key => {
|
||||
const definition = schema.definitions[key];
|
||||
if (['dialog', 'drawer'].includes(definition.type)) {
|
||||
modals.push({
|
||||
...definition,
|
||||
$$ref: key
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
JSONTraverse(schema, (value: any, key: string, host: any) => {
|
||||
if (
|
||||
key === 'actionType' &&
|
||||
@ -1031,12 +1031,7 @@ export const MainStore = types
|
||||
) {
|
||||
const key = value === 'drawer' ? 'drawer' : 'dialog';
|
||||
const body = host[key] || host['args'];
|
||||
if (
|
||||
body &&
|
||||
!body.$ref &&
|
||||
body.$$id &&
|
||||
!modals.find(m => (m as any).$$originId === body.$$id)
|
||||
) {
|
||||
if (body && !body.$ref) {
|
||||
modals.push({
|
||||
...body,
|
||||
actionType: value
|
||||
@ -1045,6 +1040,37 @@ export const MainStore = types
|
||||
}
|
||||
return value;
|
||||
});
|
||||
|
||||
// 公共组件排在前面
|
||||
Object.keys(schema.definitions || {})
|
||||
.reverse()
|
||||
.forEach(key => {
|
||||
const definition = schema.definitions[key];
|
||||
if (['dialog', 'drawer'].includes(definition.type)) {
|
||||
// 不要把已经内嵌弹窗中的弹窗再放到外面
|
||||
if (
|
||||
definition.$$originId &&
|
||||
modals.find(item => item.$$id === definition.$$originId)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
modals.unshift({
|
||||
...definition,
|
||||
$$ref: key
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 子弹窗时,自己就是个弹窗
|
||||
if (['dialog', 'drawer', 'confirmDialog'].includes(schema.type)) {
|
||||
modals.unshift({
|
||||
...schema,
|
||||
// 如果还包含这个,子弹窗里面收集弹窗的时候会出现多份内嵌弹窗
|
||||
definitions: undefined
|
||||
});
|
||||
}
|
||||
|
||||
return modals;
|
||||
},
|
||||
|
||||
|
@ -1397,7 +1397,12 @@ export const scrollToActive = debounce((selector: string) => {
|
||||
}
|
||||
}, 200);
|
||||
|
||||
export function addModal(schema: any, modal: any, definitions?: any) {
|
||||
export function addModal(
|
||||
schema: any,
|
||||
modal: any,
|
||||
definitions?: any,
|
||||
isKeyValid?: (key: string) => boolean
|
||||
) {
|
||||
schema = {...schema, definitions: {...schema.definitions}};
|
||||
|
||||
// 如果有传入definitions,则合并到schema中
|
||||
@ -1407,7 +1412,10 @@ export function addModal(schema: any, modal: any, definitions?: any) {
|
||||
|
||||
let idx = 1;
|
||||
while (true) {
|
||||
if (!schema.definitions[`modal-ref-${idx}`]) {
|
||||
if (
|
||||
!schema.definitions[`modal-ref-${idx}`] &&
|
||||
(!isKeyValid || isKeyValid(`modal-ref-${idx}`))
|
||||
) {
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
@ -1435,16 +1443,42 @@ export function addModal(schema: any, modal: any, definitions?: any) {
|
||||
*/
|
||||
export function modalsToDefinitions(
|
||||
modals: Array<EditorModalBody>,
|
||||
definitions: any = {}
|
||||
definitions: any = {},
|
||||
edtingModal?: EditorModalBody
|
||||
) {
|
||||
let schema = {
|
||||
definitions
|
||||
};
|
||||
|
||||
modals.forEach((modal, idx) => {
|
||||
if (
|
||||
edtingModal &&
|
||||
(edtingModal.$$ref
|
||||
? edtingModal.$$ref === modal.$$ref
|
||||
: edtingModal.$$id === modal.$$id)
|
||||
) {
|
||||
// 自己不需要转成 definitions
|
||||
return;
|
||||
} else if (
|
||||
!modal.$$ref &&
|
||||
modal.$$id &&
|
||||
(JSONGetById(schema.definitions, modal.$$id) ||
|
||||
(edtingModal && JSONGetById(edtingModal, modal.$$id)))
|
||||
) {
|
||||
// 内嵌弹窗,已经包含在 definitions 里面了
|
||||
// 不需要转成 definitions
|
||||
return;
|
||||
}
|
||||
|
||||
if (modal.$$ref) {
|
||||
schema.definitions[modal.$$ref] = JSONPipeIn(modal);
|
||||
} else {
|
||||
[schema] = addModal(schema, {...modal, $$originId: modal.$$id});
|
||||
[schema] = addModal(
|
||||
schema,
|
||||
{...modal, $$originId: modal.$$id},
|
||||
undefined,
|
||||
key => !modals.find(m => m.$$ref && m.$$ref === key)
|
||||
);
|
||||
}
|
||||
});
|
||||
return schema.definitions;
|
||||
|
@ -79,7 +79,7 @@ function DialogActionPanel({
|
||||
subscribeSchemaSubmit((schema: any, nodeSchema: any, id: string) => {
|
||||
const rawActions = JSONGetById(schema, id)?.onEvent[eventKey]?.actions;
|
||||
if (!rawActions || !Array.isArray(rawActions)) {
|
||||
throw new Error('动作配置错误');
|
||||
return;
|
||||
}
|
||||
|
||||
const actionSchema =
|
||||
@ -88,8 +88,9 @@ function DialogActionPanel({
|
||||
? rawActions.length - 1
|
||||
: actionIndex
|
||||
];
|
||||
const modals: Array<LocalModal> = actionSchema.__actionModals;
|
||||
const modals: Array<LocalModal> = actionSchema?.__actionModals;
|
||||
if (!Array.isArray(modals)) {
|
||||
// 不是编辑确定触发的,直接返回
|
||||
return schema;
|
||||
}
|
||||
|
||||
@ -288,6 +289,7 @@ function DialogActionPanel({
|
||||
actionSchema.actionType === 'drawer' ? 'drawer' : 'dialog'
|
||||
] || actionSchema.args;
|
||||
|
||||
const schema = store.schema;
|
||||
const modals: Array<LocalModal> = store.modals.map(modal => {
|
||||
const isCurrentActionModal = modal.$$id === dialogBody?.$$id;
|
||||
|
||||
@ -310,7 +312,11 @@ function DialogActionPanel({
|
||||
value: modal.$$id,
|
||||
modal: modal,
|
||||
isCurrentActionModal,
|
||||
data: modal.data
|
||||
data: modal.data,
|
||||
// 当前编辑的弹窗不让再里面再次弹出
|
||||
disabled: modal.$$ref
|
||||
? modal.$$ref === schema.$$ref
|
||||
: modal.$$id === schema.$$id
|
||||
};
|
||||
});
|
||||
|
||||
@ -458,7 +464,7 @@ function DialogActionPanel({
|
||||
skipForm?: boolean,
|
||||
closePopOver?: () => void
|
||||
) => {
|
||||
store.openSubEditor({
|
||||
manager.openSubEditor({
|
||||
title: '新建弹窗',
|
||||
value: {
|
||||
type: 'dialog',
|
||||
@ -513,7 +519,7 @@ function DialogActionPanel({
|
||||
if (!currentModal) {
|
||||
return;
|
||||
}
|
||||
store.openSubEditor({
|
||||
manager.openSubEditor({
|
||||
title: '编辑弹窗',
|
||||
value: {
|
||||
type: 'dialog',
|
||||
@ -526,7 +532,9 @@ function DialogActionPanel({
|
||||
],
|
||||
...(currentModal.modal as any),
|
||||
definitions: modalsToDefinitions(
|
||||
modals.filter(item => !item.isActive).map(item => item.modal)
|
||||
modals.map(item => item.modal),
|
||||
{},
|
||||
currentModal.modal
|
||||
)
|
||||
},
|
||||
onChange: ({definitions, ...modal}: any, diff: any) => {
|
||||
|
@ -321,19 +321,12 @@ export const ACTION_TYPE_TREE = (manager: any): RendererPluginAction[] => {
|
||||
e.stopPropagation();
|
||||
|
||||
const modalId = modal.$$id;
|
||||
store.openSubEditor({
|
||||
manager.openSubEditor({
|
||||
title: '编辑弹窗',
|
||||
value: {
|
||||
type: 'dialog',
|
||||
...modal,
|
||||
definitions: modalsToDefinitions(
|
||||
store.modals.filter(
|
||||
(m: any) =>
|
||||
// 不要把自己下发,不允许弹窗自己再弹出自己
|
||||
// 不要下发自己内容里面内嵌的弹窗,否则会导致子弹窗里面的弹窗列表重复
|
||||
m.$$id !== modalId && !JSONGetById(modal, m.$$id)
|
||||
)
|
||||
)
|
||||
definitions: modalsToDefinitions(store.modals, {}, modal)
|
||||
},
|
||||
onChange: ({definitions, ...modal}: any, diff: any) => {
|
||||
store.updateModal(modalId, modal, definitions);
|
||||
|
Loading…
Reference in New Issue
Block a user