mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
fix: 修复 froalaEditor 数值可能不同步的问题 (#2815)
This commit is contained in:
parent
981537867c
commit
905f891521
20
fis-conf.js
20
fis-conf.js
@ -495,7 +495,7 @@ if (fis.project.currentMedia() === 'publish') {
|
||||
'!mpegts.js/**',
|
||||
'!hls.js/**',
|
||||
'!froala-editor/**',
|
||||
'!react-froala-wysiwyg/**',
|
||||
|
||||
'!tinymce/**',
|
||||
'!zrender/**',
|
||||
'!echarts/**',
|
||||
@ -526,11 +526,7 @@ if (fis.project.currentMedia() === 'publish') {
|
||||
'!punycode/**'
|
||||
],
|
||||
|
||||
'rich-text.js': [
|
||||
'src/components/RichText.tsx',
|
||||
'froala-editor/**',
|
||||
'react-froala-wysiwyg/**'
|
||||
],
|
||||
'rich-text.js': ['src/components/RichText.tsx', 'froala-editor/**'],
|
||||
|
||||
'tinymce.js': ['src/components/Tinymce.tsx', 'tinymce/**'],
|
||||
|
||||
@ -569,7 +565,7 @@ if (fis.project.currentMedia() === 'publish') {
|
||||
'!mpegts.js/**',
|
||||
'!hls.js/**',
|
||||
'!froala-editor/**',
|
||||
'!react-froala-wysiwyg/**',
|
||||
|
||||
'!src/components/RichText.tsx',
|
||||
'!zrender/**',
|
||||
'!echarts/**',
|
||||
@ -777,7 +773,7 @@ if (fis.project.currentMedia() === 'publish') {
|
||||
'!mpegts.js/**',
|
||||
'!hls.js/**',
|
||||
'!froala-editor/**',
|
||||
'!react-froala-wysiwyg/**',
|
||||
|
||||
'!tinymce/**',
|
||||
'!zrender/**',
|
||||
'!echarts/**',
|
||||
@ -808,11 +804,7 @@ if (fis.project.currentMedia() === 'publish') {
|
||||
'!punycode/**'
|
||||
],
|
||||
|
||||
'pkg/rich-text.js': [
|
||||
'src/components/RichText.js',
|
||||
'froala-editor/**',
|
||||
'react-froala-wysiwyg/**'
|
||||
],
|
||||
'pkg/rich-text.js': ['src/components/RichText.js', 'froala-editor/**'],
|
||||
|
||||
'pkg/tinymce.js': ['src/components/Tinymce.tsx', 'tinymce/**'],
|
||||
|
||||
@ -864,7 +856,7 @@ if (fis.project.currentMedia() === 'publish') {
|
||||
'!mpegts.js/**',
|
||||
'!hls.js/**',
|
||||
'!froala-editor/**',
|
||||
'!react-froala-wysiwyg/**',
|
||||
|
||||
'!src/components/RichText.tsx',
|
||||
'!zrender/**',
|
||||
'!echarts/**',
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "amis",
|
||||
"version": "1.4.0",
|
||||
"version": "1.4.1-beta.2",
|
||||
"description": "一种MIS页面生成工具",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
@ -76,7 +76,6 @@
|
||||
"react-datetime": "2.16.2",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-dropzone": "^11.4.2",
|
||||
"react-froala-wysiwyg": "3.1.1",
|
||||
"react-input-range": "1.3.0",
|
||||
"react-json-view": "1.21.3",
|
||||
"react-overlays": "5.1.1",
|
||||
|
@ -7,7 +7,7 @@
|
||||
import React from 'react';
|
||||
|
||||
// @ts-ignore
|
||||
import FroalaEditorComponent from 'react-froala-wysiwyg';
|
||||
import FroalaEditor from 'froala-editor';
|
||||
// @ts-ignore
|
||||
import Froala from 'froala-editor/js/froala_editor.min.js';
|
||||
import 'froala-editor/js/plugins/align.min';
|
||||
@ -45,7 +45,288 @@ import 'froala-editor/js/languages/zh_cn.js';
|
||||
import 'froala-editor/css/froala_style.min.css';
|
||||
import 'froala-editor/css/froala_editor.pkgd.min.css';
|
||||
|
||||
export default class FroalaEditor extends React.Component<any, any> {
|
||||
export interface FroalaEditorComponentProps {
|
||||
config: any;
|
||||
model: string;
|
||||
onModelChange: (value: string) => void;
|
||||
}
|
||||
|
||||
// 代码来源于:https://github.com/froala/react-froala-wysiwyg/blob/master/lib/FroalaEditorFunctionality.jsx
|
||||
// 改动原因是model 同步有些问题,有时候不更新,所以基于官方代码改造一下。
|
||||
// 目前发现的问题是,如果 model 数据修改,如果此时 editor 还没有初始化完成则不会同步成功
|
||||
class FroalaEditorComponent extends React.Component<FroalaEditorComponentProps> {
|
||||
listeningEvents: any;
|
||||
element: any;
|
||||
editor: any;
|
||||
config: any;
|
||||
editorInitialized: any;
|
||||
INNER_HTML_ATTR: any;
|
||||
hasSpecialTag: any;
|
||||
oldModel: any;
|
||||
el: any;
|
||||
_initEvents: any;
|
||||
|
||||
constructor(props: FroalaEditorComponentProps) {
|
||||
super(props);
|
||||
|
||||
this.listeningEvents = [];
|
||||
this.element = null;
|
||||
this.editor = null;
|
||||
this.config = {
|
||||
immediateReactModelUpdate: false,
|
||||
reactIgnoreAttrs: null
|
||||
};
|
||||
|
||||
this.editorInitialized = false;
|
||||
this.INNER_HTML_ATTR = 'innerHTML';
|
||||
|
||||
this.oldModel = null;
|
||||
}
|
||||
|
||||
// After first time render.
|
||||
componentDidMount() {
|
||||
this.createEditor();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.destroyEditor();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (JSON.stringify(this.oldModel) == JSON.stringify(this.props.model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setContent();
|
||||
}
|
||||
|
||||
// Return cloned object
|
||||
clone(item: any) {
|
||||
const me = this;
|
||||
if (!item) {
|
||||
return item;
|
||||
} // null, undefined values check
|
||||
|
||||
let types = [Number, String, Boolean],
|
||||
result: any;
|
||||
|
||||
// normalizing primitives if someone did new String('aaa'), or new Number('444');
|
||||
types.forEach(function (type) {
|
||||
if (item instanceof type) {
|
||||
result = type(item);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof result == 'undefined') {
|
||||
if (Object.prototype.toString.call(item) === '[object Array]') {
|
||||
result = [];
|
||||
item.forEach(function (child: any, index: number, array: Array<any>) {
|
||||
result[index] = me.clone(child);
|
||||
});
|
||||
} else if (typeof item == 'object') {
|
||||
// testing that this is DOM
|
||||
if (item.nodeType && typeof item.cloneNode == 'function') {
|
||||
result = item.cloneNode(true);
|
||||
} else if (!item.prototype) {
|
||||
// check that this is a literal
|
||||
if (item instanceof Date) {
|
||||
result = new Date(item);
|
||||
} else {
|
||||
// it is an object literal
|
||||
result = {};
|
||||
for (var i in item) {
|
||||
result[i] = me.clone(item[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (false && item.constructor) {
|
||||
result = new item.constructor();
|
||||
} else {
|
||||
result = item;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = item;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
createEditor() {
|
||||
if (this.editorInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.config = this.clone(this.props.config || this.config);
|
||||
this.config = {...this.config};
|
||||
|
||||
this.element = this.el;
|
||||
|
||||
if (this.props.model) {
|
||||
this.element.innerHTML = this.props.model;
|
||||
}
|
||||
|
||||
this.setContent(true);
|
||||
|
||||
// Default initialized.
|
||||
this.registerEvent(
|
||||
'initialized',
|
||||
this.config.events && this.config.events.initialized
|
||||
);
|
||||
|
||||
// Check if events are set.
|
||||
if (!this.config.events) this.config.events = {};
|
||||
this.config.events.initialized = () => this.initListeners();
|
||||
|
||||
this.editor = new FroalaEditor(this.element, this.config);
|
||||
}
|
||||
|
||||
setContent(firstTime?: boolean) {
|
||||
if (this.props.model || this.props.model == '') {
|
||||
this.oldModel = this.props.model;
|
||||
|
||||
if (this.editorInitialized) {
|
||||
this.setNormalTagContent(firstTime);
|
||||
} else {
|
||||
this._initEvents.push(() => this.setNormalTagContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setNormalTagContent(firstTime?: boolean) {
|
||||
let self = this;
|
||||
|
||||
function htmlSet() {
|
||||
self.editor.html && self.editor.html.set(self.props.model || '');
|
||||
if (self.editorInitialized && self.editor.undo) {
|
||||
//This will reset the undo stack everytime the model changes externally. Can we fix this?
|
||||
self.editor.undo.reset();
|
||||
self.editor.undo.saveStep();
|
||||
}
|
||||
}
|
||||
|
||||
if (firstTime) {
|
||||
if (this.config.initOnClick) {
|
||||
this.registerEvent('initializationDelayed', () => {
|
||||
htmlSet();
|
||||
});
|
||||
|
||||
this.registerEvent('initialized', () => {
|
||||
this.editorInitialized = true;
|
||||
});
|
||||
} else {
|
||||
this.registerEvent('initialized', () => {
|
||||
this.editorInitialized = true;
|
||||
htmlSet();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
htmlSet();
|
||||
}
|
||||
}
|
||||
|
||||
destroyEditor() {
|
||||
if (this.element) {
|
||||
this.editor.destroy && this.editor.destroy();
|
||||
this.listeningEvents.length = 0;
|
||||
this.element = null;
|
||||
this.editorInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
getEditor() {
|
||||
if (this.element) {
|
||||
return this.editor;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
updateModel() {
|
||||
if (!this.props.onModelChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
let modelContent = '';
|
||||
|
||||
if (this.hasSpecialTag) {
|
||||
let attributeNodes = this.element.attributes;
|
||||
let attrs: any = {};
|
||||
|
||||
for (let i = 0; i < attributeNodes.length; i++) {
|
||||
let attrName = attributeNodes[i].name;
|
||||
if (
|
||||
this.config.reactIgnoreAttrs &&
|
||||
this.config.reactIgnoreAttrs.indexOf(attrName) != -1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
attrs[attrName] = attributeNodes[i].value;
|
||||
}
|
||||
|
||||
if (this.element.innerHTML) {
|
||||
attrs[this.INNER_HTML_ATTR] = this.element.innerHTML;
|
||||
}
|
||||
|
||||
modelContent = attrs;
|
||||
} else {
|
||||
let returnedHtml = this.editor.html.get();
|
||||
if (typeof returnedHtml === 'string') {
|
||||
modelContent = returnedHtml;
|
||||
}
|
||||
}
|
||||
|
||||
this.oldModel = modelContent;
|
||||
this.props.onModelChange(modelContent);
|
||||
}
|
||||
|
||||
initListeners() {
|
||||
let self = this;
|
||||
|
||||
// bind contentChange and keyup event to froalaModel
|
||||
this.editor.events.on('contentChanged', function () {
|
||||
self.updateModel();
|
||||
});
|
||||
if (this.config.immediateReactModelUpdate) {
|
||||
this.editor.events.on('keyup', function () {
|
||||
self.updateModel();
|
||||
});
|
||||
}
|
||||
|
||||
// Call init events.
|
||||
if (this._initEvents) {
|
||||
for (let i = 0; i < this._initEvents.length; i++) {
|
||||
this._initEvents[i].call(this.editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register event on jquery editor element
|
||||
registerEvent(eventName: string, callback: Function) {
|
||||
if (!eventName || !callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventName == 'initialized') {
|
||||
if (!this._initEvents) this._initEvents = [];
|
||||
this._initEvents.push(callback);
|
||||
} else {
|
||||
if (!this.config.events) {
|
||||
this.config.events = {};
|
||||
}
|
||||
|
||||
this.config.events[eventName] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<textarea ref={el => (this.el = el)}>{this.props.children}</textarea>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class extends React.Component<any, any> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
Froala.VIDEO_PROVIDERS = [
|
||||
@ -61,7 +342,6 @@ export default class FroalaEditor extends React.Component<any, any> {
|
||||
render() {
|
||||
return (
|
||||
<FroalaEditorComponent
|
||||
tag="textarea"
|
||||
config={this.props.config}
|
||||
model={this.props.model}
|
||||
onModelChange={this.props.onModelChange}
|
||||
|
Loading…
Reference in New Issue
Block a user