https://www.cnblogs.com/lovelgx/articles/11613560.html
vue 打印 前台用戶自己設計模版 打印報表實現記錄
一 需求
公司產品由cs轉向bs,我前端使用vue技術棧 ,具體的難點
1.在vue的基礎上讓用戶自己設計模版
2.設計的模版 與 后台請求的數據相結合
3.打印
二 功能實現
2.1 先說打印
問百度 web打印 出來的 基本是兩種方案 ,一是 js插件 二是 lodop 控件實現web打印功能
注: lodop 控件 需要下載一個程序。對於我們客戶來說或許有些麻煩 那就選js插件吧 lodop 官網 : http://www.mtsoftware.cn/LodopDemo.html 實現方式 百度好多 略過!!!
問百度 vue 項目實現打印 也是兩種方式
一是:通過npm 安裝插件
二是:手動下載插件到本地
擴展:詳細說下 npm安裝方式
//1.安裝 npm install vue-print-nb --save //2 main.js文件中引入 import Print from 'vue-print-nb' Vue.use(Print); //注冊 //3.使用 <div id="printTest" > <p>鋤禾日當午</p> <p>汗滴禾下土 </p> <p>誰知盤中餐</p> <p>粒粒皆辛苦</p> </div> <button v-print="'#printTest'">打印</button> //目前看 不到 手動調 的方法 //
2.2 接下來 如何設計模版
做這個模版設計,百度找了一頓的 就看見一個 注冊組件方式 ,寫的不詳細,自己沒有實現出來,(┬_┬)先回到jq的思維 !!
讓用戶或者實施人員自己設計模版,這聽起來有點之前的拖拽建站的感覺呀,拖拽元素生成對應的html代碼(不想做個這東西!!),想想有啥可以借用的東西呢?一下子想到之前用過的富文本編譯器o(∩_∩)o 哈哈!!
那就百度下-----確實找到一篇類似的文章 https://www.cnblogs.com/s0611163/p/4885833.html 文章中很多數據看不到,沒法具體的嘗試!!只能借鑒思路了 ,既然提到了 ueditor 那就也用這個吧 !!
ueditor 的官網 有不少例子 http://ueditor.baidu.com/website/onlinedemo.html
初試的時候 用的jq版本 (這個插件就是jq版的)vue的盛行 到目前已經好久不更新了,試驗完 jq版本 又 嘗試往 vue項目上 轉移 。
拓展 :Vue 中使用UEditor富文本編輯器
參考 https://blog.csdn.net/haochuan9421/article/details/81975966
//1.安裝 cnpm i vue-ueditor-wrap //2.下載處理后的UEditor,下載地址 https://github.com/HaoChuan9421/vue-ueditor-wrap/tree/master/assets/downloads //解壓,重命名文件夾為UEditor,放入public文件夾下(如果是舊項目對應static文件夾)。 //3.引用組件、注冊組件 import VueUeditorWrap from 'vue-ueditor-wrap' // ES6 Module // 或者 const VueUeditorWrap = require('vue-ueditor-wrap') // CommonJS //4. v-model綁定數據 <vue-ueditor-wrap v-model="msg"></vue-ueditor-wrap> data () { return { msg: '<h2><img src="http://img.baidu.com/hi/jx2/j_0003.gif"/>Vue + UEditor + v-model雙向綁定</h2>' } } //5. 修改配置 具體參考如下詳細代碼
//詳細代碼 <template> <div> <div id="app"> <vue-ueditor-wrap v-model="msg" :config="myConfig"></vue-ueditor-wrap> <div class="tembtn"> <!-- <el-button type="primary" @click="showOne();">獲取編輯器內容</el-button> --> <el-button type="primary" @click="saveTemplate();">保存編輯模版</el-button> </div> </div> </div> </template> <script> import VueUeditorWrap from 'vue-ueditor-wrap' export default { name: "setPrintTemplate", components: { VueUeditorWrap }, data() { return { msg:'', myConfig:{ // 編輯器不自動被內容撐高 autoHeightEnabled: false, // 初始容器高度 initialFrameHeight: 480, // 初始容器寬度 initialFrameWidth: '100%', // 上傳文件接口(這個地址是我為了方便各位體驗文件上傳功能搭建的臨時接口,請勿在生產環境使用!!!) serverUrl: '', // UEditor 資源文件的存放路徑,如果你使用的是 vue-cli 生成的項目,通常不需要設置該選項,vue-ueditor-wrap 會自動處理常見的情況,如果需要特殊配置,參考下方的常見問題2 UEDITOR_HOME_URL: '/static/UEditor/' } } }, mounted() { this.msg = localStorage.setPrintTemplate || '' }, methods: { showOne(){ alert(this.msg); }, saveTemplate(){ alert('初步計划:此處需要將保存的模版保存到數據庫中,可以設置多個,先本地模擬一個保存'); localStorage.setPrintTemplate = this.msg; alert('模版保存成功') } }, } </script>
注意:這里的msg 就是 我需要的html代碼
附件:顯示一下 測試中設計的模版 與模擬的json數據 (模版字段與json字段相同)
2.3 重點來了-----將上面的 html內容與 json想結合 需要一個函數,兩個參數 分別是模版代碼 與 json內容
這個功能算是核心功能吧 費了點盡!~~~(由於不確定哪些用循環?哪些是簡單的單獨的字段替換?表格的位置都在最后么??等等可擴展功能)
補充:在做這個功能的時候 先百度了下 js模版引擎原理 來提高點思路
文章一 : https://www.cnblogs.com/c2016c/articles/9343042.html
文章二:https://www.jianshu.com/p/9091e8a343e4
文章三:https://www.cnblogs.com/hustskyking/p/principle-of-javascript-template.html
確實提供一些思路 但我需要的功能也不近相同 ,他們都知道 循環體是哪~~我這個沒法具體的 ,寫出來循環體 只能去判斷!!
上來給自己定了個難點的 模版 (⊙﹏⊙) 如圖示:
圖中 分為5個部分,
第一部分是 單純的 p標簽 內容替換
第二四部分是 一個 table 帶有循環體(這個循環體的table我加了個 class="for"做標記)
第三部分 是一個 不帶循環的table形式(其實他的性質和 單純的p標簽一樣 算是一類)
第五部分 是結尾 沒啥!!!!
大體思路:
1.將模版分段(依據循環的表格),顯示順序不能變,放入到一個大的數組arr中
2.將arr中的每個 模版進行處理
2.1 如是 非循環部分 直接 正則替換 后形成 html內容
2.2 若是 循環部分 再次正則找出循環體, 循環替換后與該table的其他部分 組成新的html內容
設計模版的時候 要注意:
如果是循環展示列表數據 那么這個列表 應該加class="for",(編譯器切換到html模式),模版字段必須與 接口返回的數據字段一樣
對於接口:
返回的數據格式要參照上面形式
直接代碼
<script> var html = '<p>商品快遞單</p><p>時間:{{time}}</p><p>地點:{{address}}</p><p>內容:</p><p><br/></p><table class="for"><tbody><tr class="firstRow"><td width="105" valign="top" style="word-break: break-all;"> <span style="font-size: 18px;"> <strong><span style="font-size: 16px;">名稱</span></strong></span></td><td width="105" valign="top" style="word-break: break-all;">顏色</td><td width="105" valign="top" style="word-break: break-all;">大小</td><td width="105" valign="top" style="word-break: break-all;">數量</td><td width="105" valign="top" style="word-break: break-all;">途徑</td><td width="105" valign="top" style="word-break: break-all;">價格</td><td width="105" valign="top" style="word-break: break-all;">優惠價格</td><td width="105" valign="top" style="word-break: break-all;">實施人員</td></tr><tr><td width="105" valign="top" style="word-break: break-all;">{{name}}</td><td width="105" valign="top" style="word-break: break-all;">{{color}}</td><td width="105" valign="top" style="word-break: break-all;">{{size}}</td><td width="105" valign="top" style="word-break: break-all;">{{num}}</td><td width="105" valign="top" style="word-break: break-all;">{{tj}}</td><td width="105" valign="top" style="word-break: break-all;">{{price}}</td><td width="105" valign="top" style="word-break: break-all;">{{aoutprice}}</td><td width="105" valign="top" style="word-break: break-all;">{{cname}}</td></tr></tbody></table><p><br/></p><table><tbody><tr class="firstRow"><td width="483" valign="top" style="word-break: break-all;">國家</td><td width="483" valign="top" style="word-break: break-all;">{{country}}</td></tr><tr><td width="483" valign="top" style="word-break: break-all;">省份</td><td width="483" valign="top" style="word-break: break-all;">{{province}}</td></tr></tbody></table><p><br/></p><table class="for"><tbody><tr class="firstRow"><td width="483" valign="top" style="word-break: break-all;">城市</td><td width="483" valign="top" style="word-break: break-all;">姓名</td></tr><tr><td width="483" valign="top" style="word-break: break-all;">{{city}}</td><td width="483" valign="top" style="word-break: break-all;">{{name}}</td></tr></tbody></table><p><br/></p>'; var json = { time: '2018-15-15', address: '山東青島', data: [{ name: '蘋果', color: '紅色', size: '3存', num: '4', tj: '5', price: '6', aoutprice: '1', cname: '劉一', }, { name: '桃子', color: '綠色', size: '6', num: '4', tj: '5', price: '6', aoutprice: '1', cname: '劉二', }], country: '中國', province: '山東', } var efg = /<table class="for">.*?<\/table>/g; var regtr = /<tr>.*?<\/tr>/g; attachTemplateToData = function(template, data) { var i = 0, len = data.length, fragment = '', tmeparr = [], //總的模版 tempforarr = [], //用 replace 處理 得到 字段中需要循環的模版------最多就一個哈哈哈哈哈 tempsplit = []; //用 split 處理 得到的 不需要循環的模版 function strReplace(temps, obj) { var t, key, reg; //遍歷該數據項下所有的屬性,將該屬性作為key值來查找標簽,然后替換 for (key in obj) { reg = new RegExp('{{' + key + '}}', 'ig'); if (typeof(obj[key]) === 'string') { t = (t || temps).replace(reg, obj[key]); } } return t; } function forReplace(temps, obj) { var t, key, reg; //遍歷該數據項下所有的屬性,將該屬性作為key值來查找標簽,然后替換 for (key in obj) { reg = new RegExp('{{' + key + '}}', 'ig'); t = (t || temps).replace(reg, obj[key]); } console.log(t) return t; } tempsplit = template.split(efg); template.replace(efg, function(str) { tempforarr.push(str) }) for (var i = 0; i < tempsplit.length - 1; i++) { //for 循環 把 tempforarr 和 tempsplit 的模版 組成一個完整的 組數 (按照原先顯示順序的,其實就是在 tempsplit 中元素之間依次插入 tempforarr) //鑒於就一個 for循環的表格 的直接中間 插入就好 tmeparr.push(tempsplit[i]); tmeparr.push(tempforarr[i]) } tmeparr.push(tempsplit[tempsplit.length - 1]); console.log(tmeparr) //處理模版 var forTempHtml = '', forTempCont = ''; for (var m = 0; m < tmeparr.length; m++) { if (regtr.test(tmeparr[m]) && (tmeparr[m].search('table class="for"') != -1)) { //for for循環數據 //得到 循環體的模版 tmeparr[m].replace(regtr, function(str) { forTempHtml = str; }) // 將循環體的模版 與 數據結合得到 相應的 html結構 for (var n = 0; n < json.data.length; n++) { forTempCont += forReplace(forTempHtml, json.data[n]); } //得到循環后的html結構 將此結構 替換 原先的模版 fragment += tmeparr[m].replace(regtr, forTempCont) } else { //普通的字段替換 fragment += strReplace(tmeparr[m], json); } } return fragment; }; $('#cont').html(attachTemplateToData(html, json)) </script>
拓展補充:正則與字符串處理
a.*?b就是a開始b結束的匹配
//利用正則分割,str.split(/reg/); js同時使用多個分隔符分割字符串. var mystring = "jb51.net,google.com,baidu.com_weibo.com_haotu.net"; var myarray = mystring.split(/[,_]/);
//代碼中 常用的幾個正則 var re = /<%([^%>]+)?%>/g, 正則全局匹配以<%開頭,中間不是%或>並以%>結尾的配配項 var efg = /<table class="for">.*?<\/table>/g;
//目前還不知道 有啥函數可以一次性的 將模版分成5段!! //split 函數 得到的結果 不帶有 分隔符 //replace 函數可以找到 對應的分隔符 //兩者相結合 ,你來一個元素 我來一個元素 最終巧妙的 按照順序 得到了 完整的 分段模版
6666