mirror of
https://gitee.com/zoujingli/ThinkAdmin.git
synced 2026-06-07 20:48:09 +08:00
将后台富文本模块统一切换为 wangEditor 加载入口,移除 CKEditor4/CKEditor5 的动态选择与运行时编辑器驱动配置,降低前端资源体积和配置复杂度。 重写 createEditor 封装,支持初始化复用、内容同步、销毁恢复、上传配置透传和图片/视频自定义上传,并在表单提交前通过 data-editor-source 统一回写编辑器内容。 同步更新系统配置页面、接口脚本、初始化数据、语言包、测试用例和业务表单引用,将原 ckeditor 模块调用改为 editor 模块,避免遗留编辑器入口失效。
211 lines
8.1 KiB
PHP
211 lines
8.1 KiB
PHP
(function () {
|
||
const WANG_EDITOR_VERSION = '5.7.3';
|
||
const DEFAULT_HEIGHT = 500;
|
||
const DEFAULT_TOOLBAR_KEYS = [
|
||
'headerSelect',
|
||
'|',
|
||
'bold',
|
||
'underline',
|
||
'italic',
|
||
'|',
|
||
'bulletedList',
|
||
'numberedList',
|
||
'|',
|
||
'justifyLeft',
|
||
'justifyCenter',
|
||
'justifyRight',
|
||
'|',
|
||
'insertLink',
|
||
'uploadImage',
|
||
'uploadVideo',
|
||
'|',
|
||
'undo',
|
||
'redo'
|
||
];
|
||
const DEFAULT_IMAGE_HOVERBAR_KEYS = [
|
||
'imageWidth30',
|
||
'imageWidth50',
|
||
'imageWidth100',
|
||
'editorImageSizeMenu',
|
||
'editImage',
|
||
'deleteImage'
|
||
];
|
||
const DEFAULT_VIDEO_HOVERBAR_KEYS = [
|
||
'enter',
|
||
'editVideoSize',
|
||
'editVideoSrc'
|
||
];
|
||
|
||
function tip(message, type) {
|
||
message = message || '文件上传失败';
|
||
if (window.$ && $.msg && typeof $.msg.tips === 'function') return $.msg.tips(message);
|
||
if (window.layer && typeof window.layer.msg === 'function') return window.layer.msg(message);
|
||
return (type === 'error' ? console.error : console.log)(message);
|
||
}
|
||
|
||
function firstTextarea(ele) {
|
||
const $ele = $(ele).eq(0);
|
||
if ($ele.length < 1) throw new Error('Editor target not found: ' + ele);
|
||
return $ele;
|
||
}
|
||
|
||
function toNumber(value, fallback) {
|
||
value = parseFloat(value);
|
||
return Number.isFinite(value) ? value : fallback;
|
||
}
|
||
|
||
function toInteger(value, fallback) {
|
||
value = parseInt(value, 10);
|
||
return Number.isFinite(value) ? value : fallback;
|
||
}
|
||
|
||
function getConfig($ele, option) {
|
||
option = option || {};
|
||
const data = $ele.data() || {};
|
||
const upload = option.upload || {};
|
||
const path = String(upload.path || option.uploadPath || data.path || 'editor').replace(/\W/g, '');
|
||
return {
|
||
mode: option.mode || data.editorMode || 'default',
|
||
height: toInteger(option.height || data.editorHeight || data.height, DEFAULT_HEIGHT),
|
||
placeholder: option.placeholder || data.placeholder || '请输入内容...',
|
||
uploadPath: path || 'editor',
|
||
uploadType: upload.uptype || upload.type || option.uptype || data.uptype || '',
|
||
uploadSafe: (upload.safe || option.safe || data.safe) ? 1 : 0,
|
||
uploadHide: (upload.hide || option.hide || data.hload) ? 1 : 0,
|
||
uploadSize: toInteger(upload.size || option.size || data.size, 0),
|
||
uploadQuality: toNumber(upload.quality || option.quality || data.quality, 1),
|
||
imageMaxWidth: toInteger(upload.maxWidth || option.maxWidth || data.maxWidth, 0),
|
||
imageMaxHeight: toInteger(upload.maxHeight || option.maxHeight || data.maxHeight, 0),
|
||
imageCutWidth: toInteger(upload.cutWidth || option.cutWidth || data.cutWidth, 0),
|
||
imageCutHeight: toInteger(upload.cutHeight || option.cutHeight || data.cutHeight, 0),
|
||
toolbarKeys: option.toolbarKeys || data.toolbarKeys || DEFAULT_TOOLBAR_KEYS,
|
||
hoverbarKeys: option.hoverbarKeys || {
|
||
image: {menuKeys: option.imageHoverbarKeys || DEFAULT_IMAGE_HOVERBAR_KEYS},
|
||
video: {menuKeys: option.videoHoverbarKeys || DEFAULT_VIDEO_HOVERBAR_KEYS}
|
||
},
|
||
excludeKeys: option.excludeKeys || ['fullScreen']
|
||
};
|
||
}
|
||
|
||
function uploadBySystem(file, config, type, insertFn) {
|
||
if (!window.SystemUploadAdapter) {
|
||
const message = '后台上传模块尚未加载,请刷新页面后重试';
|
||
tip(message, 'error');
|
||
return Promise.reject(new Error(message));
|
||
}
|
||
|
||
file.path = config.uploadPath;
|
||
file.quality = config.uploadQuality;
|
||
file.maxWidth = type === 'image' ? config.imageMaxWidth : 0;
|
||
file.maxHeight = type === 'image' ? config.imageMaxHeight : 0;
|
||
file.cutWidth = type === 'image' ? config.imageCutWidth : 0;
|
||
file.cutHeight = type === 'image' ? config.imageCutHeight : 0;
|
||
|
||
return new Promise(function (resolve, reject) {
|
||
const $scope = $('<span></span>');
|
||
const fail = function (event, data) {
|
||
const message = (data && data.file && data.file.xstats) || '文件上传失败';
|
||
$scope.off('upload.error', fail);
|
||
tip(message, 'error');
|
||
reject(new Error(message));
|
||
};
|
||
$scope.one('upload.error', fail);
|
||
|
||
new window.SystemUploadAdapter({
|
||
elem: $scope,
|
||
hide: config.uploadHide,
|
||
safe: config.uploadSafe,
|
||
type: config.uploadType,
|
||
size: config.uploadSize
|
||
}).upload([file], function (url, uploadedFile) {
|
||
$scope.off('upload.error', fail);
|
||
uploadedFile = uploadedFile || file;
|
||
if (type === 'video') {
|
||
insertFn(url, '');
|
||
} else {
|
||
// 第三个参数是图片链接 href,后台编辑场景不自动加链接,避免点击图片跳转导致编辑丢失。
|
||
insertFn(url, uploadedFile.name || file.name || '');
|
||
}
|
||
resolve(url);
|
||
});
|
||
});
|
||
}
|
||
|
||
function buildMenuConfig(config) {
|
||
return {
|
||
uploadImage: {
|
||
async customUpload(file, insertFn) {
|
||
return uploadBySystem(file, config, 'image', insertFn);
|
||
}
|
||
},
|
||
uploadVideo: {
|
||
async customUpload(file, insertFn) {
|
||
return uploadBySystem(file, config, 'video', insertFn);
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
window.createEditor = function (ele, option) {
|
||
const $ele = firstTextarea(ele);
|
||
const exists = $ele.data('editorInstance');
|
||
if (exists) return exists;
|
||
if (!window.wangEditor || typeof window.wangEditor.createEditor !== 'function') {
|
||
throw new Error('wangEditor is not loaded.');
|
||
}
|
||
|
||
const config = getConfig($ele, option);
|
||
const $layout = $('<div class="ta-editor-layout"><div class="ta-editor-toolbar"></div><div class="ta-editor-body"></div></div>');
|
||
$layout.css({border: '1px solid #ccc', zIndex: 1001});
|
||
$layout.find('.ta-editor-toolbar').css({borderBottom: '1px solid #ccc'});
|
||
$layout.find('.ta-editor-body').css({height: config.height + 'px'});
|
||
$ele.hide().attr('data-editor-source', 'wangEditor').data('editorLayout', $layout).after($layout);
|
||
|
||
const editor = window.wangEditor.createEditor({
|
||
selector: $layout.find('.ta-editor-body').get(0),
|
||
html: $ele.val() || '<p><br></p>',
|
||
config: {
|
||
placeholder: config.placeholder,
|
||
hoverbarKeys: config.hoverbarKeys,
|
||
MENU_CONF: buildMenuConfig(config),
|
||
onCreated(editor) {
|
||
editor.setHtml($ele.val() || '<p><br></p>');
|
||
},
|
||
onChange(editor) {
|
||
$ele.val(editor.getHtml()).triggerHandler('editor.change', editor);
|
||
}
|
||
},
|
||
mode: config.mode
|
||
});
|
||
|
||
window.wangEditor.createToolbar({
|
||
editor: editor,
|
||
selector: $layout.find('.ta-editor-toolbar').get(0),
|
||
config: {toolbarKeys: config.toolbarKeys, excludeKeys: config.excludeKeys},
|
||
mode: config.mode
|
||
});
|
||
|
||
const destroy = editor.destroy.bind(editor);
|
||
editor.getData = function () {
|
||
return editor.getHtml();
|
||
};
|
||
editor.setData = function (html) {
|
||
return editor.setHtml(html || '<p><br></p>');
|
||
};
|
||
editor.updateElement = function () {
|
||
$ele.val(editor.getHtml());
|
||
return editor;
|
||
};
|
||
editor.destroy = function () {
|
||
$ele.removeData('editorInstance').removeData('editorLayout').removeAttr('data-editor-source').show();
|
||
$layout.remove();
|
||
return destroy();
|
||
};
|
||
|
||
$ele.data('editorInstance', editor).triggerHandler('editor.init', editor);
|
||
return editor;
|
||
};
|
||
|
||
window.createEditor.version = WANG_EDITOR_VERSION;
|
||
})();
|