最近做一個考試系統,編輯題目要用到富文本編輯,要求可以添加圖片、視頻和pdf。此前項目中用的都是wangEditor,使用的時候發現只可以上傳本地圖片,視頻只能添加鏈接,找了其他插件,也沒發現比較好用的。所以就看看了看源碼。
一、實現效果
實現效果
原本效果
二、源碼
下面是wangEditor實現插入視頻的代碼
function Video(editor) { this.editor = editor; this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-play"></i></div>'); this.type = 'panel'; // 當前是否 active 狀態 this._active = false; } // 原型 Video.prototype = { constructor: Video, onClick: function onClick() { this._createPanel(); }, _createPanel: function _createPanel() { var _this = this; // 創建 id var textValId = getRandom('text-val'); var btnId = getRandom('btn'); // 創建 panel var panel = new Panel(this, { width: 350, // 一個 panel 多個 tab tabs: [{ // 標題 title: '插入視頻', // 模板 tpl: '<div><input id="' + textValId + '" type="text" class="block" placeholder="\u683C\u5F0F\u5982\uFF1A<iframe src=... ></iframe>"/><div class="w-e-button-container"><button id="' + btnId + '" class="right">\u63D2\u5165</button></div></div>', // 事件綁定 events: [{ selector: '#' + btnId, type: 'click', fn: function fn() { var $text = $('#' + textValId); var val = $text.val().trim(); if (val) _this._insert(val); // 插入視頻 // 返回 true,表示該事件執行完之后,panel 要關閉。否則 panel 不會關閉 return true; } }] }] // tabs end }); // panel end // 顯示 panel panel.show(); // 記錄屬性 this.panel = panel; } // 插入視頻 _insert: function _insert(val) { var editor = this.editor; editor.cmd.do('insertHTML', val + '<p><br></p>'); } }; 復制代碼
三、實現
看完源碼之后發現挺簡單, 在創建panel的時候添加一個插入視頻的panel,完事再實現一個視頻上傳、插入編輯器就完事了,參考一下文件上傳代碼。
1. 添加插入視頻panel
修改Video.prototype._createPanel方法
_createPanel: function _createPanel() { var _this = this; var editor = this.editor; var uploadImg = editor.uploadImg; var config = editor.config; // 創建 id // 上傳視頻id var upTriggerVideoId = getRandom('up-trigger-video'); var upFileVideoId = getRandom('up-file-video'); // 插入視頻id var textValId = getRandom('text-val'); var btnId = getRandom('btn'); // tabs 的配置 var tabsConfig = [ { title: '上傳視頻或pdf', tpl: '<div class="w-e-up-img-container"><div id="' + upTriggerVideoId + '" class="w-e-up-btn"><i class="w-e-icon-upload2"></i></div><div style="display:none;"><input id="' + upFileVideoId + '" type="file" multiple="multiple" accept="application/pdf,video/*"/></div></div>', events: [{ // 觸發選擇圖片 selector: '#' + upTriggerVideoId, type: 'click', fn: function fn() { var $file = $('#' + upFileVideoId); var fileElem = $file[0]; if (fileElem) { fileElem.click(); } else { // 返回 true 可關閉 panel return true; } } }, { // 選擇圖片完畢 selector: '#' + upFileVideoId, type: 'change', fn: function fn() { var $file = $('#' + upFileVideoId); var fileElem = $file[0]; if (!fileElem) { // 返回 true 可關閉 panel return true; } // 獲取選中的 file 對象列表 var fileList = fileElem.files; if (fileList.length) { console.log(fileList); uploadImg.uploadVideo(fileList); } // 返回 true 可關閉 panel return true; } }] }, // first tab end { // 標題 title: '插入視頻', // 模板 tpl: '<div><input id="' + textValId + '" type="text" class="block" placeholder="\u683C\u5F0F\u5982\uFF1A<iframe src=... ></iframe>"/><div class="w-e-button-container"><button id="' + btnId + '" class="right">\u63D2\u5165</button></div></div>', // 事件綁定 events: [{ selector: '#' + btnId, type: 'click', fn: function fn() { var $text = $('#' + textValId); var val = $text.val().trim(); if (val) _this._insert(val); // 插入視頻 // 返回 true,表示該事件執行完之后,panel 要關閉。否則 panel 不會關閉 return true; } }] } // second tab end ]; // tabs end // 判斷 tabs 的顯示 var tabsConfigResult = []; if (config.uploadVideoServer) { // 顯示“上傳視頻” tabsConfigResult.push(tabsConfig[0]); } if (config.showLinkVideo) { // 顯示“網絡視頻” tabsConfigResult.push(tabsConfig[1]); } // 創建 panel var panel = new Panel(this, { width: 350, // 一個 panel 多個 tab tabs: tabsConfigResult // tabs end }); // panel end // 顯示 panel panel.show(); // 記錄屬性 this.panel = panel; } 復制代碼
2. 實現文件上傳
圖片上傳在UploadImg中uploadImg方法中實現,參考一下,在UploadImg中添加一個uploadVideo方法。
// 上傳視頻 UploadImg.prototype.uploadVideo: function uploadVideo(files) { var _this3 = this; if (!files || !files.length) { return; } // ------------------------------ 獲取配置信息 ------------------------------ var editor = this.editor; var config = editor.config; var uploadVideoServer = config.uploadVideoServer; var maxSize = config.uploadVideoMaxSize; var maxSizeM = maxSize / 1024 / 1024; var maxLength = config.uploadVideoMaxLength || 10000; var uploadFileName = config.uploadFileName || ''; var uploadVideoParams = config.uploadVideoParams || {}; var uploadVideoParamsWithUrl = config.uploadVideoParamsWithUrl; var uploadVideoHeaders = config.uploadVideoHeaders || {}; var hooks = config.uploadVideoHooks || {}; var timeout = config.uploadVideoTimeout || 30 * 60 * 1000; // 30分鍾 var withCredentials = config.withCredentials; if (withCredentials == null) { withCredentials = false; } var customUploadVideo = config.customUploadVideo; if (!customUploadVideo) { // 沒有 customUploadVideo 的情況下,需要如下兩個配置才能繼續進行圖片上傳 if (!uploadVideoServer) { return; } } // ------------------------------ 驗證文件信息 ------------------------------ var resultFiles = []; var errInfo = []; arrForEach(files, function (file) { var name = file.name; var size = file.size; // chrome 低版本 name === undefined if (!name || !size) { return; } if (/\.(pdf|rm|rmvb|3gp|avi|mpeg|mpg|mkv|dat|asf|wmv|flv|mov|mp4|ogg|ogm)$/i.test(name) === false) { // 后綴名不合法,不是視頻 errInfo.push('\u3010' + name + '\u3011\u4E0D\u662F\u56FE\u7247'); return; } if (maxSize < size) { // 上傳視頻過大 errInfo.push('\u3010' + name + '\u3011\u5927\u4E8E ' + maxSizeM + 'M'); return; } // 驗證通過的加入結果列表 resultFiles.push(file); }); // 拋出驗證信息 if (errInfo.length) { this._alert('視頻驗證未通過: \n' + errInfo.join('\n')); return; } if (resultFiles.length > maxLength) { this._alert('一次最多上傳' + maxLength + '個視頻'); return; } // ------------------------------ 自定義上傳 ------------------------------ if (customUploadVideo && typeof customUploadVideo === 'function') { customUploadVideo(resultFiles, this.insertLinkVideo.bind(this)); // 阻止以下代碼執行 return; } // 添加圖片數據 var formdata = new FormData(); arrForEach(resultFiles, function (file) { var name = uploadFileName || file.name; formdata.append(name, file); }); // ------------------------------ 上傳圖片 ------------------------------ if (uploadVideoServer && typeof uploadVideoServer === 'string') { // 添加參數 var uploadVideoServerArr = uploadVideoServer.split('#'); uploadVideoServer = uploadVideoServerArr[0]; var uploadVideoServerHash = uploadVideoServerArr[1] || ''; objForEach(uploadVideoParams, function (key, val) { // 因使用者反應,自定義參數不能默認 encode ,由 v3.1.1 版本開始注釋掉 // val = encodeURIComponent(val) // 第一,將參數拼接到 url 中 if (uploadVideoParamsWithUrl) { if (uploadVideoServer.indexOf('?') > 0) { uploadVideoServer += '&'; } else { uploadVideoServer += '?'; } uploadVideoServer = uploadVideoServer + key + '=' + val; } // 第二,將參數添加到 formdata 中 formdata.append(key, val); }); if (uploadVideoServerHash) { uploadVideoServer += '#' + uploadVideoServerHash; } // 定義 xhr var xhr = new XMLHttpRequest(); xhr.open('POST', uploadVideoServer); // 設置超時 xhr.timeout = timeout; xhr.ontimeout = function () { // hook - timeout if (hooks.timeout && typeof hooks.timeout === 'function') { hooks.timeout(xhr, editor); } _this3._alert('上傳視頻超時'); }; // 監控 progress if (xhr.upload) { xhr.upload.onprogress = function (e) { var percent = void 0; // 進度條 var progressBar = new Progress(editor); if (e.lengthComputable) { percent = e.loaded / e.total; progressBar.show(percent); } }; } // 返回數據 xhr.onreadystatechange = function () { var result = void 0; if (xhr.readyState === 4) { if (xhr.status < 200 || xhr.status >= 300) { // hook - error if (hooks.error && typeof hooks.error === 'function') { hooks.error(xhr, editor); } // xhr 返回狀態錯誤 _this3._alert('上傳視頻發生錯誤', '\u4E0A\u4F20\u56FE\u7247\u53D1\u751F\u9519\u8BEF\uFF0C\u670D\u52A1\u5668\u8FD4\u56DE\u72B6\u6001\u662F ' + xhr.status); return; } result = xhr.responseText; if ((typeof result === 'undefined' ? 'undefined' : _typeof(result)) !== 'object') { try { result = JSON.parse(result); } catch (ex) { // hook - fail if (hooks.fail && typeof hooks.fail === 'function') { hooks.fail(xhr, editor, result); } _this3._alert('上傳視頻失敗', '上傳視頻返回結果錯誤,返回結果是: ' + result); return; } } if (!hooks.customInsert && result.errno != '0') { // hook - fail if (hooks.fail && typeof hooks.fail === 'function') { hooks.fail(xhr, editor, result); } // 數據錯誤 _this3._alert('上傳視頻失敗', '上傳視頻返回結果錯誤,返回結果 errno=' + result.errno); } else { if (hooks.customInsert && typeof hooks.customInsert === 'function') { // 使用者自定義插入方法 hooks.customInsert(_this3.insertLinkVideo.bind(_this3), result, editor); } else { // 將圖片插入編輯器 var data = result.data || []; data.forEach(function (link) { _this3.insertLinkVideo(link); }); } // hook - success if (hooks.success && typeof hooks.success === 'function') { hooks.success(xhr, editor, result); } } } }; // hook - before if (hooks.before && typeof hooks.before === 'function') { var beforeResult = hooks.before(xhr, editor, resultFiles); if (beforeResult && (typeof beforeResult === 'undefined' ? 'undefined' : _typeof(beforeResult)) === 'object') { if (beforeResult.prevent) { // 如果返回的結果是 {prevent: true, msg: 'xxxx'} 則表示用戶放棄上傳 this._alert(beforeResult.msg); return; } } } // 自定義 headers objForEach(uploadVideoHeaders, function (key, val) { xhr.setRequestHeader(key, val); }); // 跨域傳 cookie xhr.withCredentials = withCredentials; // 發送請求 xhr.send(formdata); } } 復制代碼
3. 插入編輯器
插入視頻也寫在UploadImg.prototype中
// 根據鏈接插入視頻 insertLinkVideo: function insertLinkVideo(link) { if (!link) return; var _this2 = this; var editor = this.editor; var config = editor.config; // 校驗格式 var linkVideoCheck = config.linkVideoCheck; var checkResult = void 0; if (linkVideoCheck && typeof linkVideoCheck === 'function') { checkResult = linkVideoCheck(link); if (typeof checkResult === 'string') { // 校驗失敗,提示信息 alert(checkResult); return; } } editor.cmd.do('insertHTML', '<iframe src="' + link + '" style="width:650px;height: 366px" frameborder="0"></iframe>'); } 復制代碼
4. 添加視頻上傳的默認參數
在config中添加一些上傳視頻默認參數, 加不加無所謂,在使用方法中都做了參數判斷,為了規范代碼,這里添加一下。
// 是否顯示添加網絡視頻的 tab showLinkVideo: true, // 插入網絡視頻的回調 linkVideoCallback: function linkVideoCallback(url) { // console.log(url) // url 即插入視頻的地址 }, // 默認上傳視頻 max size: 512M uploadVideoMaxSize: 512 * 1024 * 1024, // 配置一次最多上傳幾個視頻 uploadVideoMaxLength: 5, // 上傳視頻的自定義參數 uploadVideoParams: { // token: 'abcdef12345' }, // 上傳視頻的自定義header uploadVideoHeaders: { // 'Accept': 'text/x-json' }, // 自定義上傳視頻超時時間 30分鍾 uploadVideoTimeout: 30 * 60 * 1000, // 上傳視頻 hook uploadVideoHooks: { // customInsert: function (insertLinkVideo, result, editor) { // console.log('customInsert') // // 視頻上傳並返回結果,自定義插入視頻的事件,而不是編輯器自動插入視頻 // const data = result.data1 || [] // data.forEach(link => { // insertLinkVideo(link) // }) // }, before: function before(xhr, editor, files) { // 視頻上傳之前觸發 // 如果返回的結果是 {prevent: true, msg: 'xxxx'} 則表示用戶放棄上傳 // return { // prevent: true, // msg: '放棄上傳' // } }, success: function success(xhr, editor, result) { // 視頻上傳並返回結果,視頻插入成功之后觸發 }, fail: function fail(xhr, editor, result) { // 視頻上傳並返回結果,但視頻插入錯誤時觸發 }, error: function error(xhr, editor) { // 視頻上傳出錯時觸發 }, timeout: function timeout(xhr, editor) { // 視頻上傳超時時觸發 } } 復制代碼
四、使用
初始化編輯器,配置上傳視頻參數
var editor = new window.wangEditor('#text-editor'); // 圖片上傳 editor.customConfig.uploadImgServer = 'upload/editUpload'; // 上傳接口 editor.customConfig.uploadFileName = 'files'; // 上傳文件參數名 editor.customConfig.uploadImgHooks = { // 上傳完成處理方法 customInsert: function (insertImg, result) { if (result.ret === 200) { (result.data || '').split(',').forEach(function (link) { link && insertImg(link); }); } else { flavrShowByTime('上傳失敗', null, 'danger'); } } }; // 視頻上傳 editor.customConfig.uploadVideoServer = 'editUpload'; // 上傳接口 editor.customConfig.uploadVideoHooks = { // 上傳完成處理方法 customInsert: function (insertVideo, result) { if (result.ret === 200) { (result.data || '').split(',').forEach(function (link) { link && insertVideo(link); }); } else { flavrShowByTime('上傳失敗', null, 'danger'); } } }; editor.create(); 復制代碼
五。轉載自:
https://www.lagou.com/lgeduarticle/99623.html