window 搜狗输入法下编辑器占位符和内容重叠问题
这种情况是,tinymce插件库存在一些兼容BUG,需要我们自行手写样式或者js替换掉placeholder,代码如下:
// 获取富文本框的内容
const handleChange = (editorContent) => {
// console.log('>>>>> TinymceEditor handleChange', editorContent);
// console.log('>>>>> TinymceEditor handleChange', editorContent, editorRef.current);
window.email____detail__content = editorContent;
onChange(editorContent);
if (window.tinymce?.Env.os.isWindows()) {
if (editorContent) {
editorRef.current?.getBody().removeAttribute('data-mce-placeholder');
} else {
editorRef.current
?.getBody()
.setAttribute('data-mce-placeholder', placeholder || intl.formatMessage({ id: 'email.inputHolder' }));
}
}
};
render区域代码:
<div className={cn(styles.TinymceEditorWrap, language)} style={style}>
<Editor
onScriptsLoad={() => {
// 初始化字间距插件
PluginManagerLetterSpacing();
}}
// tinymceScriptSrc={'./tinymce/tinymce.min.js'} // 本地开发可以采用这个
tinymceScriptSrc={'https://vv-public-staticresource.vv.cn/tinymce/tinymce.min.js'}
onInit={(evt, editor) => {
editorRef.current = editor;
editor.getBody().style.fontSize = '16px';
// console.log('>>>>>>>>> onInit editor', editor);
}}
ref={ref}
value={initData}
style={contentStyle}
init={{
icons: 'vv',
menubar: false, // 菜单栏
statusbar: false, // 底部状态栏
branding: false, // 是否显示版权信息
resize: false,
language,
height,
toolbar,
// toolbar_mode: 'wrap', // 工具栏展开方式 'floating', 'sliding', 'scrolling', or 'wrap'。 Default 'floating'
plugins,
font_size_formats: '8px 10px 12px 14px 16px 18px 24px 36px 48px',
font_size_input_default_unit: 'px',
images_file_types: 'png,jpg,jpeg,gif',
file_picker_types: 'image',
automatic_uploads: true,
images_upload_handler: myUploadFn,
placeholder: placeholder || intl.formatMessage({ id: 'email.inputHolder' }),
content_style:
".mce-content-body{font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;font-size: 16px;color: #49536C;line-height: 28px;font-weight: 400}" +
'.mce-content-body[data-mce-placeholder]::before {color:#BCC1CD !important;}' +
'img {max-width: 100%; height: auto;}' +
".divider{display:flex;justify-content:space-between;align-items:center;}.divider:before,.divider:after{content:'';width:50px;height:2px;background: #f0f0f0;}" +
'.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width:0px"],table[style*="border-width:0px"] caption,table[style*="border-width:0px"] td,table[style*="border-width:0px"] th{border:0 !important;}',
// cache_suffix: '?v=4.1.6',
}}
onEditorChange={handleChange}
onDragOver={onDragOver}
onClick={handleCloseExpandedDom}
/>
</div>
完整的富文本组件代码:
import styles from './index.module.less';
import { useIntl } from 'react-intl';
import cn from 'classnames';
import { forwardRef, memo, useEffect, useRef } from 'react';
import { message } from '@/components';
import { Editor } from '@tinymce/tinymce-react';
import { uploadFn } from './utils';
import PluginManagerLetterSpacing from './plugins/letterspacing';
import { useSafeState } from '@vv-desktop-web-core/hooks';
import { jsbridge } from '@vv-desktop-web-core/helpers';
/**
* 需要返回什么功能按需添加
* https://www.tiny.cloud/docs/tinymce/6/react-ref/
* @param height
* @param initData
* @param onChange
* @returns {JSX.Element}
* @constructor
*/
const TinymceEditor = forwardRef(
(
{
height = '100%',
initData = '',
onChange,
onDragOver = () => {},
style = {},
contentStyle = {},
placeholder = '',
},
ref,
) => {
const intl = useIntl();
// 部分被css重置的文案是写在css里的,依据class language区分
const [language, setLanguage] = useSafeState('zh-CN');
const editorRef = useRef();
// eslint-disable-next-line no-param-reassign
height = typeof height === 'number' ? `${height}px` : height;
// console.log('>>>>>> TinymceEditor 组件', 'initData', initData);
// 不允许添加尺寸大于50M的图片
const myValidateFn = (file) => {
if (file.size > 1024 * 1024 * 50) {
message.error('Size is greater than 50M!');
return false;
}
return true;
};
const myUploadFn = (blobInfo) => {
return new Promise((resolve, reject) => {
const file = blobInfo.blob();
const { type } = file;
if (!myValidateFn(file)) {
// eslint-disable-next-line prefer-promise-reject-errors
reject('Size is greater than 50M!');
return;
}
// 图片大小 <500k 则走base64方式,>= 则走oos方式
if (file.size < 1024 * 500) {
const img = `data:${type};base64,${blobInfo.base64()}`;
resolve(img);
} else {
uploadFn({
file,
onChange: (d) => console.log('>>>>>>> onChange', d),
onSuccess: (d) => {
resolve(d);
},
onError: () => {
reject('上传失败');
},
onProgress: (d) => console.log('>>>>>>> onProgress', d),
});
}
});
};
// https://www.tiny.cloud/docs/tinymce/6/plugins/
const plugins = ['link', 'image', 'code', 'media', 'emoticons', 'lists', 'letterspacing'];
// https://www.tiny.cloud/docs/tinymce/6/toolbar-configuration-options/#toolbar
// https://www.tiny.cloud/docs/tinymce/6/available-toolbar-buttons/
const toolbar =
'undo redo | fontsize | lineheight | letterspacing | ' +
'forecolor bold italic underline strikethrough | ' +
'superscript subscript removeformat emoticons | ' +
'indent outdent alignleft aligncenter alignright alignjustify | ' +
'blocks | ' +
'bullist numlist blockquote code | ' +
'image link unlink hr | remove';
// 获取富文本框的内容
const handleChange = (editorContent) => {
// console.log('>>>>> TinymceEditor handleChange', editorContent, editorRef.current);
window.email____detail__content = editorContent;
onChange(editorContent);
if (window.tinymce?.Env.os.isWindows()) {
if (editorContent) {
editorRef.current?.getBody().removeAttribute('data-mce-placeholder');
} else {
editorRef.current
?.getBody()
.setAttribute('data-mce-placeholder', placeholder || intl.formatMessage({ id: 'email.inputHolder' }));
}
}
};
// 非toolbar区域,需要关闭toolbar浮窗
const handleCloseExpandedDom = () => {
const expandedDom = document.querySelector('.tox-tbtn.tox-tbtn--enabled');
if (expandedDom) {
expandedDom.click();
}
};
useEffect(() => {
const language1 = jsbridge?.getLocale();
if (language1 && language1?.includes('en')) {
setLanguage('en');
}
}, []);
return (
<div className={cn(styles.TinymceEditorWrap, language)} style={style}>
<Editor
onScriptsLoad={() => {
// 初始化字间距插件
PluginManagerLetterSpacing();
}}
// tinymceScriptSrc={'./tinymce/tinymce.min.js'} // 本地开发可以采用这个
tinymceScriptSrc={'https://xx.xx.xx/tinymce/tinymce.min.js'} // cdn资源存放访问地址
onInit={(evt, editor) => {
editorRef.current = editor;
editor.getBody().style.fontSize = '16px';
// console.log('>>>>>>>>> onInit editor', editor);
}}
ref={ref}
value={initData}
style={contentStyle}
init={{
icons: 'vv',
menubar: false, // 菜单栏
statusbar: false, // 底部状态栏
branding: false, // 是否显示版权信息
resize: false,
language,
height,
toolbar,
// toolbar_mode: 'wrap', // 工具栏展开方式 'floating', 'sliding', 'scrolling', or 'wrap'。 Default 'floating'
plugins,
font_size_formats: '8px 10px 12px 14px 16px 18px 24px 36px 48px',
font_size_input_default_unit: 'px',
images_file_types: 'png,jpg,jpeg,gif',
file_picker_types: 'image',
automatic_uploads: true,
images_upload_handler: myUploadFn,
placeholder: placeholder || intl.formatMessage({ id: 'email.inputHolder' }),
content_style:
".mce-content-body{font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;font-size: 16px;color: #49536C;line-height: 28px;font-weight: 400}" +
'.mce-content-body[data-mce-placeholder]::before {color:#BCC1CD !important;}' +
'img {max-width: 100%; height: auto;}' +
".divider{display:flex;justify-content:space-between;align-items:center;}.divider:before,.divider:after{content:'';width:50px;height:2px;background: #f0f0f0;}" +
'.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width:0px"],table[style*="border-width:0px"] caption,table[style*="border-width:0px"] td,table[style*="border-width:0px"] th{border:0 !important;}',
// cache_suffix: '?v=4.1.6',
}}
onEditorChange={handleChange}
onDragOver={onDragOver}
onClick={handleCloseExpandedDom}
/>
</div>
);
},
);
export default memo(TinymceEditor);