
老后臺管理系統(tǒng)里業(yè)務(wù)方最愛提的一個需求就是Excel數(shù)據(jù)粘貼到網(wǎng)頁端的UEditor編輯框還要把邊框、底色、合并單元格都帶過來。很多人問怎么配置ueditor才能支持這個操作。說實話這問題在社區(qū)里被問過無數(shù)次官方文檔對Excel粘貼卻講得很少。UEditor作為一款經(jīng)典的富文本編輯器剪貼板處理邏輯默認(rèn)偏保守粘貼過來的一整張Excel表格經(jīng)常變成堆在一起的純文本或者表格進(jìn)來了但樣式被過濾得干干凈凈。這篇文章就把“ueditor配置Excel數(shù)據(jù)粘貼”這件事從頭到尾拆一遍重點落在配置項、過濾規(guī)則和常見坑上適合維護(hù)傳統(tǒng)后臺系統(tǒng)、或者還在用UEditor的老項目前端參考。1. 先把原理弄清楚Excel粘貼到編輯器到底發(fā)生了什么1.1 剪貼板里不只有純文本Excel中選中一片區(qū)域按CtrlC復(fù)制到剪貼板的內(nèi)容不一定只有文字。Windows下剪貼板會同時存好幾種格式純文本、帶制表符的CSV、HTML片段還可能有OLE富文本數(shù)據(jù)。瀏覽器執(zhí)行粘貼事件時contenteditable區(qū)域會優(yōu)先讀取HTML/富文本格式的數(shù)據(jù)因為只有這樣才能把“表格”這種結(jié)構(gòu)還原出來。UEditor的核心編輯區(qū)是一個iframe里的contenteditable區(qū)域所以當(dāng)用戶按下CtrlV時瀏覽器把剪貼板里的HTML片段插入進(jìn)去。Excel生成的HTML片段一般是一整段以table開頭的結(jié)構(gòu)包含tr、td甚至還會帶上注釋語法。在Chrome里這個HTML通常還帶著inline style比如寬度、背景色、邊框樣式。聽上去很美好但UEditor在接收這段HTML之后還會疊加好幾層自定義處理大部分“貼上就丟”的情況就發(fā)生在這個過程中。1.2 默認(rèn)情況下表格為什么總是“丟”UEditor對粘貼內(nèi)容不是直接接受的它默認(rèn)會走一次“清洗”流程beforepaste事件觸發(fā)UEditor拿到剪貼板的HTML根據(jù)pasteplain配置判斷是否要轉(zhuǎn)純文本對HTML做標(biāo)簽與屬性過濾filterInput移除敏感標(biāo)簽和不在白名單里的屬性最后再插入到編輯器DOM中。問題就出在第2和第3步。如果ueditor.config.js里把pasteplain設(shè)成了true粘貼Excel必然變成純文本表格結(jié)構(gòu)全部丟失。就算pasteplain是false第3步的過濾規(guī)則如果不放行table/tr/td/th以及border/width/style這些屬性表格結(jié)構(gòu)即使沒被拆散也會被剝成一堆沒有邊框、沒有寬度的素表格。還要留意老版本一個容易誤踩的配置retainOnlyPastedText。這個參數(shù)在部分1.4.x版本或改造后的UEditor擴(kuò)展中用于控制粘貼時是否只保留文本。如果被誤開和pasteplain效果類似Excel內(nèi)容過來后同樣只剩純文本。排查時如果pasteplain已經(jīng)是false就要順手把這個也檢查一下。1.3 配置前先明確目標(biāo)效果動手改配置之前建議先和業(yè)務(wù)方把“粘貼后要保留到什么程度”確認(rèn)清楚不然很容易返工。通常有三個檔次只要表格結(jié)構(gòu)粘貼后能分列、分行邊框樣式無所謂。這個最容易放行標(biāo)簽即可。保留基礎(chǔ)樣式要有邊框、背景色、合并單元格效果。需要在過濾白名單里保留style等屬性。完全還原Excel樣式包括字體、行高、列寬、顏色、條件格式。這個不建議通過粘貼配置解決因為UEditor的編輯區(qū)不是Excel渲染引擎硬摳最終效果也達(dá)不到反而會拖慢編輯器。確定目標(biāo)后再去看配置項就能對號入座。多數(shù)后臺系統(tǒng)只需要第二個檔次也就是“保留表格結(jié)構(gòu)和基礎(chǔ)樣式”下面章節(jié)的配置也主要圍繞這個目標(biāo)展開。2. 核心配置項拆解從 pasteplain 到過濾白名單2.1 第一個要檢查的開關(guān)這個配置很容易被忽略。很多老項目從模板里抄配置時會寫一行pasteplain: true意思就是“粘貼一律按純文本處理”。業(yè)務(wù)方之前吐槽“Excel粘貼不了”八成是這一個開關(guān)的原因。需要在ueditor.config.js里確認(rèn)window.UEDITOR_CONFIG { // 是否啟用純文本粘貼。true表示任何粘貼內(nèi)容都會去掉格式 pasteplain: false };也可以在初始化時覆蓋var ue UE.getEditor(editor, { pasteplain: false });判斷是否生效最簡單的方法復(fù)制一段帶加粗的文本粘貼后看strong標(biāo)簽還在不在。如果粗體還在說明編輯器對粘貼的富文本做了保留如果成了一片沒有格式的純文本那pasteplain大概率被設(shè)置成了true或者在頁面其它JS里被動態(tài)改過。toolbar里的pasteplain按鈕也要注意。UEditor默認(rèn)工具欄里如果放了“純文本粘貼”按鈕用戶在編輯器里點一下會動態(tài)切換粘貼模式。這個切換結(jié)果不會改ueditor.config.js里的原值而是通過編輯器內(nèi)部execCommand(pasteplain)切換。所以用戶側(cè)操作造成的狀態(tài)排查時也要考慮進(jìn)去。2.2 給表格標(biāo)簽和屬性開放行名單UEditor為了安全在ueditor.all.js里維護(hù)了一套xssFilterRules本質(zhì)是一個HTML標(biāo)簽屬性的白名單。粘貼進(jìn)來的HTML會經(jīng)過白名單過濾不在名單里的標(biāo)簽或?qū)傩詴粍兊?。這個環(huán)節(jié)是Excel表格樣式丟失的主要區(qū)域。默認(rèn)白名單里通常有table和td這些標(biāo)簽但屬性未必齊全。例如table上常見的border、cellpadding、cellspacingtd上的rowspan、colspan、align、valign幾乎所有元素上的style屬性默認(rèn)可能被過濾。配置思路不是把xssFilterRules整體關(guān)掉那是拿安全換方便在管理后臺里非常不可取。正確做法是給白名單補齊需要用到的屬性。假如你用的是1.4.x版本可以直接在初始化時這樣補UE.getEditor(editor, { pasteplain: false, xssFilterRules: { allowList: { table: [border, cellpadding, cellspacing, width, height, style], caption: [align, style], tr: [align, valign, style], td: [width, height, colspan, rowspan, align, valign, style], th: [width, height, colspan, rowspan, align, valign, style] } } });注意不同小版本的UEditor里xssFilterRules內(nèi)部字段名可能有差異有的版本是allowList有的版本是whiteList。動手前先打開你工程里的ueditor.all.js確認(rèn)一下實際字段名再用對應(yīng)名字覆蓋避免配置無效。另外還有一組filterTxtRules主要用于粘貼純文本時的字符級清洗和Excel表格樣式關(guān)系不大。排查時可以看一眼是否被改造過但正常情況下不需要動它。2.3 初始化參數(shù)、全局配置和動態(tài)改法UEditor有兩層配置來源。第一層是ueditor.config.js里默認(rèn)導(dǎo)出的window.UEDITOR_CONFIG第二層是初始化時傳入的配置對象比如UE.getEditor(editor, { ... })。初始化參數(shù)會在內(nèi)部合并并覆蓋默認(rèn)配置所以在頁面里針對單個編輯器做差異化粘貼配置放在初始化參數(shù)里是最穩(wěn)的。有些情況下編輯器已經(jīng)初始化完不方便再加參數(shù)這時候可以用兩種方式動態(tài)干預(yù)var ue UE.getEditor(editor, { pasteplain: false }); ue.ready(function() { // 運行時再改配置 ue.setOpt({ pasteplain: false }); }); // 監(jiān)聽粘貼事件做自定義處理 ue.addListener(beforepaste, function(type, html) { // 這里可以拿到即將插入的HTML做二次加工 });beforepaste事件是后期做定制最常用的入口也是后面清洗Excel樣式的主要戰(zhàn)場。把核心配置項理一張表排查時對照看很方便配置項作用常見錯誤配置pasteplain是否默認(rèn)純文本粘貼為true時Excel粘貼必然變純文本xssFilterRules粘貼HTML的標(biāo)簽/屬性白名單缺style、border、colspan導(dǎo)致樣式和結(jié)構(gòu)丟失autoClearEmptyNode是否自動清理空標(biāo)簽節(jié)點為true時空的td被刪除表格列對不齊initialStyle/ 頁面CSS編輯區(qū)默認(rèn)樣式不配置時Excel粘貼表格觀感不可控3. 實操把Excel數(shù)據(jù)粘貼功能完整配置起來3.1 最小可用配置從復(fù)制Excel到粘貼出表格下面這份配置我在實際項目里驗證過可以直接對照著改。var ue UE.getEditor(editor, { pasteplain: false, // 保留表格結(jié)構(gòu)和基礎(chǔ)屬性 xssFilterRules: { allowList: { table: [border, cellpadding, cellspacing, width, style], tr: [align, valign, style], td: [width, height, colspan, rowspan, align, valign, style], th: [width, height, colspan, rowspan, align, valign, style] } }, // 避免粘貼后自動清除空節(jié)點 autoClearEmptyNode: false });這里特別提一下autoClearEmptyNode。UEditor默認(rèn)會清理內(nèi)容為空的節(jié)點但一個正常的Excel表格里經(jīng)常有空白單元格對應(yīng)的就是td/td。如果開了空節(jié)點清理這些空格子會被直接刪除導(dǎo)致整個表格形狀移位。業(yè)務(wù)方反饋“粘貼后列對不齊”很多時候不是粘貼邏輯問題而是這里把空td吞了。改完配置后做一次基礎(chǔ)驗證打開Excel復(fù)制一個3行4列的表格回到編輯器頁面按CtrlV。正常效果應(yīng)該是編輯區(qū)內(nèi)出現(xiàn)一個邊框可見的表格單元格可以點擊編輯行和列基本與Excel一致。3.2 處理Excel粘貼后遺留的Office私有樣式第一次配置成功的人下一個動作往往是放大瀏覽器看效果然后發(fā)現(xiàn)表格上帶了很多奇怪的行內(nèi)樣式這些樣式大多來自Excel生成的HTML。比如td styleborder:solid #ccc 1.0pt;padding:5.0pt 5.0pt 5.0pt 5.0pt;mso-border-alt:solid windowtext .5pt;mso-padding-alt:0cm 5.4pt 0cm 5.4ptmso-開頭的樣式是Office特有的瀏覽器不認(rèn)識還會讓保存后的HTML變得很長。建議在beforepaste階段做一次輕量清洗把mso-私有屬性去掉同時把Excel給td加的一堆內(nèi)邊距統(tǒng)一成自己的樣式ue.addListener(beforepaste, function(type, html) { if (!html || !html.html) return; // 去掉 Office 私有樣式前綴屬性 html.html html.html.replace(/mso-[a-zA-Z0-9-]:([^;]);?/g, ); // 把Excel默認(rèn)的0.5pt邊框統(tǒng)一替換為常用1px邊框 html.html html.html.replace(/border:[^;]*;?/g, border:1px solid #ccc;); });注意beforepaste事件里的html參數(shù)是一個對象結(jié)構(gòu)在不同小版本里不完全一樣常見的處理是直接操作html.html。如果你用的版本拿不到html.html可以打印一下html對象看結(jié)構(gòu)或者改為監(jiān)聽afterpaste在HTML插入后再用editor.body做DOM遍歷同樣能達(dá)到效果。但清洗放在beforepaste的好處是編輯器內(nèi)部還會再走一遍過濾不會把你處理過的內(nèi)容再加工一次。3.3 讓粘貼表格的顯示樣式可控Excel自帶的行內(nèi)樣式經(jīng)常會干擾頁面原有風(fēng)格所以工程里我更建議“粘貼后統(tǒng)一走一套表格CSS”。在ueditor.config.js里有initialStyle可以給編輯區(qū)域?qū)懭胱远x樣式或者直接在當(dāng)前頁面的樣式中覆蓋編輯區(qū)table/* 給編輯區(qū)里的表格一個默認(rèn)觀感 */ .edui-body-content table { border-collapse: collapse; width: 100%; } .edui-body-content table td, .edui-body-content table th { border: 1px solid #ddd; padding: 6px 8px; }配合上一步把Excel行內(nèi)樣式清掉表格就會按這套CSS渲染而不是各家Excel表格各長各的樣。要是業(yè)務(wù)方要求保留合并單元格和背景色那就在beforepaste清洗時只清mso-*不要把background-color和colspan也剝掉。3.4 從粘貼到入庫的完整鏈路驗證配置完以后不要只在編輯頁看到表格顯示正常就收工要回到“保存-取數(shù)-回顯”的完整鏈路里驗證一遍。點保存時UEditor的getContent()會把編輯區(qū)HTML取出來Excel粘貼進(jìn)來的表格結(jié)構(gòu)會原樣輸出里面可能帶著style屬性。后端入庫前注意做一次HTML轉(zhuǎn)義和XSS過濾不要相信前端UEditor的過濾能擋住所有攻擊。尤其是帶著style的標(biāo)簽后端還是要按自己的安全策略再過一遍?;仫@時把數(shù)據(jù)庫內(nèi)容直接塞回UEditor的初始內(nèi)容或者編輯時重新setContent()表格通常能正常顯示。如果發(fā)現(xiàn)回顯后表格越過編輯區(qū)域、寬度失控多半是保存的HTML里沒有外層容器約束檢查一下table的width是否被設(shè)置成了固定像素值必要時在getContent之后再統(tǒng)一替換為百分比。這一步驗證很值得做。很多項目粘貼時看著正常一到數(shù)據(jù)回顯就不對最后查出問題是線上代碼里的ueditor.config.js流傳版本和本地不一樣粘貼過濾規(guī)則根本沒有生效。4. 常見問題與排查技巧實錄4.1 粘貼Excel后全部變成純文本先查這四處看到這個現(xiàn)象按優(yōu)先級排查看編輯器工具欄上的“純文本粘貼”按鈕是否處于激活狀態(tài)。如果是再點一次解除。檢查ueditor.config.js里的pasteplain是否為false??错撁胬锸欠裨诰庉嬈鞒跏蓟笥謭?zhí)行過editor.execCommand(pasteplain)。如果都不是檢查是否有全局監(jiān)聽把粘貼事件攔截了比如某些前端框架的防粘貼擴(kuò)展。這四處都查完基本能定位問題。最隱蔽的是第三種有些項目里為了“統(tǒng)一編輯器行為”會在公共初始化腳本里強(qiáng)制開啟純文本粘貼后來某個頁面需要Excel粘貼就在業(yè)務(wù)側(cè)寫了pasteplain: false但公共腳本在初始化后又被調(diào)了一次把配置覆蓋回去了。4.2 表格結(jié)構(gòu)在但邊框和背景色沒了怎么辦多半是過濾白名單里沒有放行style或者放行了但清洗正則把background-color也一并清了。處理方式在xssFilterRules中給table/td/th/tr補上style屬性。如果你自己寫了替換邊框的正則檢查正則是否過于激進(jìn)比如把整個style值都刪了。建議清洗時只針對mso-*不要用“把style整體刪掉”這種一刀切辦法。還有一個經(jīng)驗Excel粘貼出來的表格邊框通常寫在td上而不是table上。所以如果業(yè)務(wù)方反饋“只有外框、單元格之間沒有線”優(yōu)先檢查td的style是否被過濾而不是盯著table的border屬性。4.3 粘貼的表格把頁面撐破了寬度怎么控制Excel生成的HTML里table上經(jīng)常帶著固定總寬度比如width1000或者stylewidth:100.0pt這個寬度在小屏電腦上會直接撐破編輯區(qū)。建議在beforepaste里把table的width統(tǒng)一處理成百分比或者限制最大值ue.addListener(beforepaste, function(type, html) { if (!html || !html.html) return; html.html html.html.replace(/table([^]*)/g, function(match, attrs) { attrs attrs.replace(/width[^]*/g, ); attrs attrs.replace(/style([^]*)/g, function(s, style) { style style.replace(/width:\s*\d(pt|px)/g, width:100%); return style style ; }); return table attrs ; }); });這樣處理之后表格寬度跟著編輯區(qū)走不會再看Excel原始列寬。要注意的是單元格的寬度也要做類似處理否則表格總寬度收住了個別td的固定像素寬仍然會把內(nèi)容擠變形。4.4 瀏覽器差異Firefox粘貼Excel效果不對怎么辦Excel粘貼在不同瀏覽器里的呈現(xiàn)差異很大。Chrome表現(xiàn)最直接粘貼出來的HTML基本可用Firefox對剪貼板富文本的支持弱一些用戶在Firefox里粘貼Excel有時只能拿到帶制表符的純文本表現(xiàn)為“看起來沒分列、全擠在一行”。這種情況不是UEditor配置能完全兜住的常見做法是引導(dǎo)用戶使用Chrome或者在粘貼后識別到內(nèi)容里沒有table但出現(xiàn)了多個Tab符自動做一次\t到表格單元格的轉(zhuǎn)換ue.addListener(beforepaste, function(type, html) { var text html.html || ; if (text.indexOf(table) -1 text.indexOf(\t) -1) { // 按制表符拆行拆列拼一個簡易表格 } });這個方案可以用來兜底Firefox用戶的體驗但要注意如果用戶粘貼的是普通文本里面恰好有Tab符號可能會被誤判。建議加上確認(rèn)邏輯或者只對明確指定來源的粘貼內(nèi)容做轉(zhuǎn)換。4.5 二次編輯時表格樣式被重排別慌排查過濾白名單有的業(yè)務(wù)方會要求粘貼進(jìn)來的表格后續(xù)再次進(jìn)入編輯模式繼續(xù)改。UEditor在setContent()回填內(nèi)容時會走一次自己的過濾規(guī)則如果之前存入數(shù)據(jù)庫的HTML里包含了不在白名單里的屬性二次保存時這些屬性可能被剝掉。解決思路是回顯后如果發(fā)現(xiàn)表格樣式和第一次保存時不一致就把過濾白名單里缺的屬性補上再重新驗證“保存-回顯-再保存”的完整往返過程。只在前端繞過過濾而不動白名單下次編輯還會遇到同樣問題。在我經(jīng)手的幾個老項目里最省心的做法其實是“粘貼時盡量保留結(jié)構(gòu)樣式交給編輯區(qū)CSS接管”。因為Excel的mso私有樣式真的很難控與其和它較勁不如只保留合并單元格、對齊這些關(guān)鍵信息邊框和底色讓頁面自己統(tǒng)一。這樣配置一次后面業(yè)務(wù)方不管貼什么樣的Excel都不會亂維護(hù)成本也低。