UNPKG

mdui.editor

Version:

Material Design 样式的富文本编辑器

216 lines (215 loc) 8.2 kB
import $ from 'mdui.jq/es/$'; import ajax from 'mdui.jq/es/functions/ajax'; import 'mdui.jq/es/methods/find'; import 'mdui.jq/es/methods/html'; import 'mdui.jq/es/methods/insertAfter'; import 'mdui.jq/es/methods/is'; import 'mdui.jq/es/methods/next'; import 'mdui.jq/es/methods/on'; import 'mdui.jq/es/methods/remove'; import 'mdui.jq/es/methods/val'; import 'mdui.jq/es/methods/trigger'; import 'mdui/es/components/dialog/alert'; import 'mdui/es/components/dialog/dialog'; import mdui from 'mdui/es/mdui'; import 'mdui/es/jq_extends/static/guid'; import MenuAbstract from '../abstracts/menuAbstract'; import { memoryFormat } from '../utils/format'; class Image extends MenuAbstract { constructor(editor, $button) { super(editor, $button); /** * <input type="file"/> 元素 */ this.$input = $(); /** * <input type="file"/> 元素的 ID */ this.inputID = $.guid(); /** * 允许上传的图片后缀 */ this.suffixs = []; /** * 允许上传的图片格式 */ this.accepts = []; this.setAccepts(); this.setInput(); this.bindInputChange(); this.bindKeyboardEvent(); } bindKeyboardEvent() { this.$container.on('keydown', (event) => { const keyCode = event.keyCode; this.selection.saveRange(); const $curElem = this.selection.getContainerElem(); if (keyCode === 8 || keyCode === 46) { if ($curElem.is('figcaption')) { // 在 figcaption 元素中按删除键时,若该元素内容为空,则不删除 const html = $curElem.html().toLowerCase().trim(); if (!html || html === '<br>' || html === '<br/>') { $curElem.html(''); event.preventDefault(); } } } if ($curElem.is('figure')) { if (keyCode === 8 || keyCode === 46) { // 删除图片时,删除 figure 元素,并聚焦到下一行 this.selection.createRangeByElem($curElem.next()); $curElem.remove(); this.selection.restore(); } event.preventDefault(); } if (keyCode === 13) { // 在 figcaption 中按回车键时,跳出图片元素,聚焦到下一个 root 元素 if ($curElem.is('figcaption')) { const $nextElem = this.selection.getRootElem().next(); if (!$nextElem.length) { // 没有下一个元素,新增一行 this.command.do('insertAfterRoot', '<p><br></p>'); } else { // 有下一个元素,聚焦到下一行 this.selection.createRangeByElem($nextElem); this.selection.restore(); } event.preventDefault(); } } }); this.$container.on('keyup', (event) => { const keyCode = event.keyCode; const $curElem = this.selection.getContainerElem(); if (keyCode === 8 || keyCode === 46) { // 在 figcaption 中删除时,若该元素不含文字,则清空该元素 if ($curElem.is('figcaption')) { const html = $curElem.html().toLowerCase().trim(); if (!html || html === '<br>' || html === '<br/>') { $curElem.html(''); } } } }); } /** * 设置允许上传的图片类型 */ setAccepts() { this.suffixs = this.editor.options.imageUploadSuffix; const map = { png: 'image/png', jpg: 'image/jpeg', gif: 'image/gif', }; this.accepts = this.suffixs.map((suffix) => map[suffix]); } /** * 在按钮后面插入 <input type="file"/> 元素 */ setInput() { this.$input = $(`<input type="file" id="${this.inputID}" name="${this.editor.options.imageUploadName}" accept="${this.accepts.join(', ')}"/>`).insertAfter(this.$button); } /** * 选择文件后触发的事件 */ bindInputChange() { this.$input.on('change', (event) => { // @ts-ignore const files = event.target.files; if (!files.length) { return; } this.upload(files[0]); this.$input.val(''); }); } /** * 执行上传 * @param file */ upload(file) { if (this.accepts.indexOf(file.type) < 0) { mdui.alert(`仅允许上传 ${this.suffixs.join(', ')} 格式的图片`); return; } if (this.editor.options.imageUploadMaxSize && file.size > this.editor.options.imageUploadMaxSize) { mdui.alert(`图片体积不能超过 ${memoryFormat(this.editor.options.imageUploadMaxSize)}`); return; } const formData = new FormData(); formData.append(this.editor.options.imageUploadName, file); let loadingDialog; let uploadTime; let uploadTimeInterval; ajax({ url: this.editor.options.imageUploadUrl, method: 'POST', data: formData, processData: false, dataType: 'json', contentType: false, global: false, beforeSend: (xhr) => { uploadTime = 0; uploadTimeInterval = setInterval(() => (uploadTime += 100), 100); loadingDialog = mdui.dialog({ title: '上传中…', content: '<p class="mdui_editor-upload-progress">0%</p>', history: false, modal: true, cssClass: 'mdui_editor-upload-progress-dialog', }); const $progress = loadingDialog.$element.find('.mdui_editor-upload-progress'); xhr.upload.onprogress = (event) => { // @ts-ignore $progress.html(`${((event.loaded / event.total) * 100).toFixed(0)}%`); }; }, complete: () => { clearInterval(uploadTimeInterval); if (uploadTime < 500) { setTimeout(() => { loadingDialog.close(); }, 500 - uploadTime); } else { loadingDialog.close(); } }, }) .then((response) => { if (this.editor.options.imageUploadResponseTransform) { response = this.editor.options.imageUploadResponseTransform(response); } if (response.code) { mdui.alert(response.message); return; } const $rootElem = this.selection.getRootElem(); const rootHTML = $rootElem.html().toLowerCase().trim(); const imgHTML = `<figure><img src="${response.data.url}"/><figcaption placeholder="图片描述(选填)"></figcaption></figure>`; if ($rootElem[0].nodeName === 'P' && (rootHTML === '<br>' || rootHTML === '<br/>')) { // 当前为空的 p 元素,替换该元素 this.command.do('replaceRoot', imgHTML); } else { // 当前不是空的 p 元素,在当前元素后面插入图片 this.command.do('insertAfterRoot', imgHTML); } // 在图片下面重新插入一行,并聚焦 this.command.do('insertAfterRoot', '<p><br></p>'); }) .catch(() => mdui.alert('图片上传失败')); } onclick() { $(`#${this.inputID}`).trigger('click'); } } Image.icon = 'image'; Image.title = '插入图片'; export default Image;