feat:编辑器移动端支持单独编辑 (#11052)

* feat:编辑器移动端支持单独编辑

* fix: ts报错

---------

Co-authored-by: zhangxulong <zhangxulong@baidu.com>
This commit is contained in:
ls 2024-10-15 13:57:35 +08:00 committed by GitHub
parent d8759d2186
commit eaf5217c4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 105 additions and 8 deletions

View File

@ -4,6 +4,7 @@
import {JSONValueMap, findObjectsWithKey} from './utils/helper';
import isPlainObject from 'lodash/isPlainObject';
import merge from 'lodash/merge';
const isMobile = (window as any).matchMedia?.('(max-width: 768px)').matches
? true
: false;
@ -22,6 +23,11 @@ export const envOverwrite = (schema: any, locale?: string) => {
delete newValue[locale];
return newValue;
} else if (isMobile && value.mobile) {
let schemaNodes = findObjectsWithKey(value, 'mobile');
for (let schemaNode of schemaNodes) {
merge(schemaNode, schemaNode['mobile']);
}
const newValue = Object.assign({}, value, value.mobile);
delete newValue.mobile;
return newValue;

View File

@ -26,6 +26,8 @@ export interface EditorProps extends PluginEventListener {
onChange: (value: SchemaObject) => void;
preview?: boolean;
isMobile?: boolean;
/** 用于区分响应式页面和h5页面 */
isMobileAloneEdit?: boolean;
isSubEditor?: boolean;
autoFocus?: boolean;
className?: string;
@ -182,6 +184,7 @@ export default class Editor extends Component<
this.store = MainStore.create(
{
isMobile: props.isMobile,
isMobileAloneEdit: props.isMobileAloneEdit,
theme: props.theme,
toolbarMode: props.toolbarMode || 'default',
noDialog: props.noDialog,
@ -591,6 +594,7 @@ export default class Editor extends Component<
const {
preview,
isMobile,
isMobileAloneEdit,
className,
theme,
appLocale,
@ -649,6 +653,7 @@ export default class Editor extends Component<
{...previewProps}
editable={!preview}
isMobile={isMobile}
isMobileAloneEdit={isMobileAloneEdit}
store={this.store}
manager={this.manager}
theme={theme}
@ -670,6 +675,7 @@ export default class Editor extends Component<
appLocale={appLocale}
amisEnv={amisEnv}
readonly={readonly}
isMobile={isMobile}
/>
)}

View File

@ -11,6 +11,8 @@ import {
resizeSensor
} from 'amis';
import {isAlive} from 'mobx-state-tree';
import {JSONMergeForMobile} from '../util';
import cloneDeep from 'lodash/cloneDeep';
/**
* observer
@ -24,6 +26,7 @@ export interface IFramePreviewProps {
manager: EditorManager;
/** 应用语言类型 */
appLocale?: string;
isMobileAloneEdit?: boolean;
}
@observer
export default class IFramePreview extends React.Component<IFramePreviewProps> {
@ -98,8 +101,20 @@ export default class IFramePreview extends React.Component<IFramePreviewProps> {
}
render() {
const {editable, store, appLocale, autoFocus, env, data, manager, ...rest} =
this.props;
const {
editable,
store,
appLocale,
autoFocus,
env,
data,
manager,
isMobileAloneEdit,
...rest
} = this.props;
const schema = editable
? store.filteredSchema
: store.filteredSchemaForPreview;
return (
<Frame
@ -111,7 +126,7 @@ export default class IFramePreview extends React.Component<IFramePreviewProps> {
<InnerComponent store={store} editable={editable} manager={manager} />
<div ref={this.dialogMountRef} className="ae-Dialog-preview-mount-node">
{render(
editable ? store.filteredSchema : store.filteredSchemaForPreview,
isMobileAloneEdit ? JSONMergeForMobile(cloneDeep(schema)) : schema,
{
...rest,
key: editable ? 'edit-mode' : 'preview-mode',

View File

@ -22,6 +22,14 @@ export default class CodeEditorPanel extends React.Component<PanelProps> {
}, 500);
}
@autobind
onChange(...rest: any) {
const {store} = this.props;
store.patchCodeEdit(true);
this.props.onChange(rest);
store.patchCodeEdit(false);
}
render() {
const {onChange, manager, store} = this.props;
@ -31,7 +39,7 @@ export default class CodeEditorPanel extends React.Component<PanelProps> {
<div className="ae-CodePanel-content">
<AMisCodeEditor
value={store.valueWithoutHiddenProps}
onChange={onChange}
onChange={this.onChange}
$schema={store.jsonSchemaUri}
$schemaUrl={manager.config.$schemaUrl}
onPaste={this.handleCodePaste}

View File

@ -9,6 +9,8 @@ import {autobind, isHasPluginIcon} from '../../util';
import {findDOMNode} from 'react-dom';
import {PanelItem} from '../../plugin';
import {WidthDraggableBtn} from '../base/WidthDraggableBtn';
import type {Schema} from 'amis';
import merge from 'lodash/merge';
interface RightPanelsProps {
store: EditorStoreType;
@ -17,6 +19,7 @@ interface RightPanelsProps {
appLocale?: string;
amisEnv?: any;
readonly?: boolean;
isMobile?: boolean;
}
interface RightPanelsStates {
@ -72,6 +75,12 @@ export class RightPanels extends React.Component<
manager.panelChangeValue(...arg);
}
@autobind
mergeMobileAttribute(obj: Schema) {
const {isMobile} = this.props;
return obj.mobile && isMobile ? merge(obj, obj.mobile) : obj;
}
render() {
const {store, manager, theme, readonly} = this.props;
const {isOpenStatus, isFixedStatus} = this.state;
@ -86,7 +95,7 @@ export class RightPanels extends React.Component<
info: node?.info,
path: node?.path,
node: node,
value: store.value,
value: this.mergeMobileAttribute(store.value),
onChange: this.handlePanelChangeValue,
store: store,
manager: manager,
@ -100,7 +109,7 @@ export class RightPanels extends React.Component<
id={id}
info={node?.info}
path={node?.path}
value={store.value}
value={this.mergeMobileAttribute(store.value)}
onChange={this.handlePanelChangeValue}
store={store}
manager={manager}

View File

@ -41,6 +41,7 @@ export interface PreviewProps {
className?: string;
editable?: boolean;
isMobile?: boolean;
isMobileAloneEdit?: boolean;
store: EditorStoreType;
manager: EditorManager;
data?: any;
@ -522,6 +523,7 @@ export default class Preview extends Component<PreviewProps> {
amisEnv,
theme,
isMobile,
isMobileAloneEdit,
autoFocus,
toolbarContainer,
appLocale,
@ -578,6 +580,7 @@ export default class Preview extends Component<PreviewProps> {
manager={manager}
autoFocus={autoFocus}
appLocale={appLocale}
isMobileAloneEdit={isMobileAloneEdit}
></IFramePreview>
) : (
<SmartPreview

View File

@ -1869,6 +1869,7 @@ export class EditorManager {
trailing: true
});
isCodeEditing = false;
patching = false;
patchingInvalid = false;
patchSchema(force = false) {
@ -1906,6 +1907,10 @@ export class EditorManager {
this.patchingInvalid && this.patchSchema(force);
}
patchCodeEdit(isCodeEdit: boolean) {
this.isCodeEditing = isCodeEdit;
}
/**
* region hack
*/

View File

@ -149,6 +149,7 @@ export const MainStore = types
.model('EditorRoot', {
ready: false, // 异步组件加载前不可用
isMobile: false,
isMobileAloneEdit: false,
isSubEditor: false,
// 用于自定义爱速搭中的 amis 文档路径
amisDocHost: types.optional(types.string, 'https://baidu.gitee.io'),
@ -251,7 +252,8 @@ export const MainStore = types
/** 应用语料 */
appCorpusData: types.optional(types.frozen(), {}),
/** 应用多语言状态,用于其它组件进行订阅 */
appLocaleState: types.optional(types.number, 0)
appLocaleState: types.optional(types.number, 0),
isCodeEditing: types.optional(types.boolean, false)
})
.views(self => {
return {
@ -2314,6 +2316,10 @@ export const MainStore = types
beforeDestroy() {
lazyUpdateTargetName.cancel();
},
patchCodeEdit(isEdit: boolean) {
self.isCodeEditing = isEdit;
}
};
});

View File

@ -969,7 +969,15 @@ function applyChange(target: any, source: any, change: DiffChange) {
source
}
);
const editorStore = (window as any)?.editorStore;
// pc 响应式页面纯h5页面不处理
if (
editorStore.isMobileAloneEdit &&
editorStore.isMobile &&
!editorStore.isCodeEditing
) {
change.path.unshift('mobile');
}
DeepDiff.applyChange(target, source, change);
}
@ -2023,3 +2031,34 @@ export const RAW_TYPE_MAP: {
'user-select': 'user',
'department-select': 'department'
};
/*
* mobile对象中的属性合并到最外层
* @param obj
* @returns
*/
export function JSONMergeForMobile(obj: any): any {
if (!isObject(obj)) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(JSONMergeForMobile);
}
if (obj.mobile) {
merge(obj, obj.mobile, {$$id: obj.$$id});
}
Object.keys(obj).forEach(key => {
let prop = obj[key];
if (Array.isArray(prop)) {
prop.map((item: any) => {
JSONMergeForMobile(item);
});
} else {
JSONMergeForMobile(prop);
}
});
return obj;
}