原理:
和多叉樹的原理相似,參考了它的展現形式。javascript
表頭說明:
若是沒有孩子節點就只返回以下一個字段:php
若是有孩子節點,就把數據加在children裏面,層層嵌套,返回字段以下:css
數據結構格式,參考以下代碼:html
headerData:[ { name: '地區', }, { name: '總數據', children: [ { name: '數據1', children: [ { name: '數據11', children: [ { name: '數據111', }, { name: '數據112', } ] }, { name: '數據12', children: [ { name: '數據121', }, { name: '數據122', } ] }, { name: '數據13', children: [ { name: '數據131', }, { name: '數據132', } ] }, { name: '數據14', }, ] } ] } ];
表頭的寬高方面,前端計算,後端不用管,按照以下格式返回數據便可。前端
每一項按照表頭展現的順序返回,經過數組的形式
返回一個參數:vue
數據結構格式參考代碼以下:java
bodyData:[ ["地區最早","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ["地區","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ["地區","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ["地區","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ["地區","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ["地區","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ["地區","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ["地區","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ["地區","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ["地區最後","數據111","數據112","數據121","數據122","數據131","數據132","數據14"], ]
如上表頭與表格數據代碼生成的效果如圖:python
語法高亮用到 codemirror 插件react
/** * 遞歸遍歷 格式化數組 * @param { Array } paramArr 目標數組 * @param { Number } level 層級 */ export function formatArray(paramArr, level) { let levelFirst = Number(level) const arr = [] let childArr = [] for (let i = 0; i < paramArr.length; i++) { let obj = {} for (let j in paramArr[i]) { if (j != 'children') { obj[j] = paramArr[i][j] } obj['level'] = levelFirst obj['width'] = getLeafCountTree(paramArr[i]) if (!paramArr[i].children) { obj['childrenNumber'] = 0 // LeafNode: 葉子節點就是樹中最底段的節點 // obj['isLeafNode'] = true } else { // obj['isLeafNode'] = false obj['childrenNumber'] = paramArr[i].children.length } } arr.push(obj) if (paramArr[i].children) { let lev = Number(levelFirst) + 1 childArr = childArr.concat(formatArray(paramArr[i].children, lev)); } } let endArr = arr.concat(childArr) return endArr }
/** * 獲取 節點的全部葉子節點個數 * @param {Object} json Object對象 */ export function getLeafCountTree(json) { if(!json.children){ return 1; }else{ var leafCount = 0; for(var i = 0 ; i < json.children.length ; i++){ leafCount = leafCount + getLeafCountTree(json.children[i]); } return leafCount; } }
// json對對象字符串的格式化,美化 export function jsonFromat (text_value){ if(text_value == ""){ alert("不能爲空"); return false; } else { var json=eval('(' + text_value + ')'); text_value=JSON.stringify(json); var res=""; for(var i=0,j=0,k=0,ii,ele;i<text_value.length;i++) {//k:縮進,j:""個數 ele=text_value.charAt(i); if(j%2==0&&ele=="}") { k--; for(ii=0;ii<k;ii++) ele=" "+ele; ele="\n"+ele; } else if(j%2==0&&ele=="{") { ele+="\n"; k++; for(ii=0;ii<k;ii++) ele+=" "; } else if(j%2==0&&ele==",") { ele+="\n"; for(ii=0;ii<k;ii++) ele+=" "; } else if(ele=="\"") j++; res+=ele; } return res } }
<template> <div class="pages-tables " id="pages-tables"> <div class="textarea"> <h1 class="title">複雜表頭 json 數據格式驗證:</h1> <p class="message">表頭展現效果以下:</p> <div class="rolling-table auto-table" ref="tableBox" :style="{height: maxHeight + 'px'}"> <table class="table" id="table" cellpadding="0" cellspacing="0" ref="rollingTable"> <thead ref="thead"> <tr v-for="(x,i) in headerList" :key="i"> <th class="rows " :class="{'cross': index == 0 && i == 0}" v-for="(l,index) in x" :key="index" :colspan="l.width" :rowspan="l.height">{{l.name}}</th> </tr> </thead> <tbody> <tr v-for="(b,i) in bodyList" :key="i + 'a'"> <template v-for="(x, index) in b"> <td :class="{'cols': index == 0 }" :key="index + 'b'"> {{ x | valueFromt }} </td> </template> </tr> <tr></tr> </tbody> </table> </div> <p class="message">提示:輸入 json 覆蓋原來的便可,且有驗證 json 格式是否正確的功能</p> <div class="control"> <!-- <div class="content fl"> <p>select a theme: <select @change="selectTheme" v-model="selected"> <option>default</option> <option>night</option> <option>monokai</option> <option>neat</option> <option>elegant</option> <option>cobalt</option> <option>eclipse</option> <option>rubyblue</option> <option>lesser-dark</option> <option>xq-dark</option> </select> </p> </div> <div class="content fl ml20"> <p>select the editor language: <select @change="selectMode" v-model="mode"> <option>javascript</option> <option>php</option> <option>python</option> <option>vue</option> <option>xml</option> <option>sql</option> <option>http</option> <option>css</option> <option>sass</option> <option>jsx</option> <option>django</option> </select> </p> </div> <div class="content fl ml20"> <p>select keyMap: <select @change="selectKeyMap" v-model="keyMap"> <option>default</option> <option>emacs</option> <option>sublime</option> <option>vim</option> </select> </p> </div> --> <div class="fl ml20 mt20 mb10 submit"> <mt-button type="primary" size="small" @click.native="setInputValue">提交</mt-button> </div> <div class="clearfix"></div> </div> <textarea id="code" name="code"> [ { name: '地區', }, { name: '總數據', children: [ { name: '數據1', children: [ { name: '數據11', children: [{ name: '數據111', }, { name: '數據112', } ] }, { name: '數據12', children: [{ name: '數據121', }, { name: '數據122', } ] }, { name: '數據13', children: [{ name: '數據131', }, { name: '數據132', } ] }, { name: '數據14', }, { name: '數據15', }, { name: '數據16數據16數據16數據16', }, { name: '數據17', }, ] } ] } ]; </textarea> </div> </div> </template> <script> // 說明這個 demo 是給 pc 端用的,單位要爲 px import { formatArray, getLeafCountTree, jsonFromat } from "libs/common/common"; import { Button, MessageBox } from 'mint-ui'; import * as CodeMirror from 'codemirror/lib/codemirror' // 根據設置的主題,引入相應的主題包,主題包存儲在theme下,使用其餘主題包時設置option中theme爲對應主題 import 'codemirror/lib/codemirror.css' import 'codemirror/theme/monokai.css' import 'codemirror/theme/neat.css' import 'codemirror/theme/elegant.css' import 'codemirror/theme/night.css' import 'codemirror/theme/cobalt.css' import 'codemirror/theme/eclipse.css' import 'codemirror/theme/rubyblue.css' import 'codemirror/theme/xq-dark.css' // styleActiveLine: 設置光標所在行高亮true/false,需引入工具包: import 'codemirror/addon/selection/active-line' // 根據設置的編輯器語言,引入相應工具包,如下爲經常使用語言包 import 'codemirror/mode/javascript/javascript' import 'codemirror/mode/go/go' import 'codemirror/mode/php/php' import 'codemirror/mode/python/python' import 'codemirror/mode/http/http' import 'codemirror/mode/sql/sql' import 'codemirror/mode/vue/vue' import 'codemirror/mode/xml/xml' import 'codemirror/mode/css/css' import 'codemirror/mode/sass/sass' import 'codemirror/mode/jsx/jsx' import 'codemirror/mode/django/django' // keyMap:快捷鍵,default使用默認快捷鍵,除此以外包括emacs,sublime,vim快捷鍵,使用需引入工具 import 'codemirror/keymap/sublime.js' import 'codemirror/keymap/emacs.js' import 'codemirror/keymap/vim.js' // extraKeys 快捷鍵,例如 {「Ctrl-Q」: 「autocomplete」}:自動補全使用須要引入工具 import 'codemirror/addon/hint/show-hint' import 'codemirror/addon/hint/javascript-hint' import 'codemirror/addon/hint/sql-hint' import 'codemirror/addon/hint/html-hint' import 'codemirror/addon/hint/xml-hint' import 'codemirror/addon/hint/anyword-hint' import 'codemirror/addon/hint/css-hint' import 'codemirror/addon/hint/show-hint' export default { data() { return { mapArray: [], keyMap: 'default', mode: 'javascript', editor: '', selected: 'monokai', header: '', maxHeight: '100%', theadHeight: '100%', offsetHeight: 0, scroll: { scroller: null }, headerList: [], bodyList: [], } }, filters: { valueFromt: function (value) { let realValue = '' if (!value) return '' value = value.toString() if (value.length > 20) { realValue = value.slice(0, 15) + '...' } else { realValue = value } return realValue }, }, methods: { selectKeyMap(){ this.editor.addKeyMap(this.keyMap) }, selectMode(){ this.editor.setOption("mode",this.mode) }, selectTheme() { this.editor.setOption("theme", this.selected); }, setInputValue() { this.header = this.editor.getValue(); if(this.header){ this.change() } }, change() { try { const newData = formatArray(eval(this.header), 0) let maxLevel = newData[newData.length - 1].level this.setHeight(newData, maxLevel + 1) this.arayLayered(newData, maxLevel) this.headerList = this.arayLayered(newData, maxLevel) } catch (e) { console.log('e:', e) MessageBox('提示', '請檢查 json 格式是否正確!!!'); } }, setHeight(arr, maxLevel) { // console.log("setHeight maxLevel", maxLevel) for (let i = maxLevel; i >= 0; i--) { for (let j = 0; j < arr.length; j++) { // 設置高 if (arr[j].childrenNumber) { arr[j].height = 1 } else { arr[j].height = maxLevel - arr[j].level } } } return arr }, arayLayered(arr, maxLevel) { let returnArr = [] for (let i = 0; i <= maxLevel; i++) { let arrLevel = [] for (let j = 0; j < arr.length; j++) { if (arr[j].level == i) { arrLevel.push(arr[j]) } } returnArr[i] = arrLevel } return returnArr } }, mounted() { let bodyListA = [ ["地區最早", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ["地區", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ["地區", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ["地區", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ["地區", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ["地區", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ["地區", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ["地區", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ["地區", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ["地區最後", "數據111", "數據112", "數據121", "數據122", "數據131", "數據132", "數據14"], ] const data = [ { name: '地區', }, { name: '總數據', children: [ { name: '數據1', children: [ { name: '數據11', children: [{ name: '數據111', }, { name: '數據112', } ] }, { name: '數據12', children: [{ name: '數據121', }, { name: '數據122', } ] }, { name: '數據13', children: [{ name: '數據131', }, { name: '數據132', } ] }, { name: '數據14', }, { name: '數據15', }, { name: '數據16數據16數據16數據16', }, { name: '數據17', }, ] } ] } ]; this.header = jsonFromat(JSON.stringify(data)) const newData = formatArray(data, 0) let maxLevel = newData[newData.length - 1].level this.setHeight(newData, maxLevel + 1) this.arayLayered(newData, maxLevel) this.headerList = this.arayLayered(newData, maxLevel) this.editor = CodeMirror.fromTextArea(document.getElementById("code"), { // value : data, // 文本域默認顯示的文本 lineNumbers: true, /* 定義是否顯示行號 */ mode: "javascript", /* 定義語法的類型,若是是html則爲:text/html */ theme: "monokai", /* 定義主題 */ smartIndent: true, // 是否智能縮進 styleActiveLine: true, keymap:"defaule" }); // this.editor.on("changes",() =>{ // //編譯器內容更改事件 // this.setInputValue(); // }); this.editor.setSize(1200,500) } } </script> <style lang="less" > .CodeMirror { border: 1px solid black; line-height: 16px; font-size: 16px; text-align: left; } </style> <style lang="less" scoped> .submit{ margin-left: 580px; margin-top: 20px; } textarea{ width: 1300px; height: 1300px; } .content{ font-size: 16px; } .textarea{ text-align: center; font-size: 20px; .title{ margin-top: 20px; font-size: 30px; color: #333; } textarea{ border: 1px solid #eee; font-size: 16px; resize: both; width: 800px; min-height: 900px; } } .message{ color: red; font-size: 16px; } .waterMask { position: absolute; width: 100%; height: 100%; z-index: 4; pointer-events: none; } .pages-tables { -webkit-overflow-scrolling: touch; // ios滑動順暢 position: relative; margin-left: 5%; padding-bottom: 160px; margin: 0 auto; width: 1200px; } .rolling-table { height: 100%; font-size: 0.28rem; color: #86939a; background-color: #fff; width: 100%; -webkit-overflow-scrolling: touch; position: relative; top: 0; // overflow: hidden; } .rows { position: relative; z-index: 3; } .cross { position: relative; z-index: 5; } table td { border: 0px solid #000; font-size: 25px; background: #fff; } ::-webkit-scrollbar { display: none; } .table { border-collapse: collapse; //去掉重複的border color: #86939e; font-size: 25px; border: 0px solid #000; min-height: 100%; text-align: center; td { border-bottom: 1px solid #eee; height: 30px; line-height: 30px; padding: 0 0.2rem; // white-space: nowrap; white-space: inherit; max-width: 500px; min-width: 50px; // overflow:hidden; // text-overflow:ellipsis; // -webkit-line-clamp:2; } th { color: #43484d; white-space: pre-wrap; height: 36px; line-height: 36px; padding: 5px 6px; background-color: #f3f4f6; font-weight: normal; padding-bottom: 0; padding-top: 0; max-width: 200px; border: 1px solid red; &:last-child{ // border-right: 0rem solid #e4e8f5; } } } tr{ position: relative; background-color: #fff; &:nth-of-type(odd){ td{ background-color: #ebf9fc; } } } </style>
效果連接以下:ios
動態效果:
對 全棧開發 有興趣的朋友能夠掃下方二維碼關注個人公衆號 —— 愛寫bugger的阿拉斯加
分享 web 開發相關的技術文章,熱點資源,全棧程序員的成長之路。
你們一塊兒交流成長。
只要關注公衆號並回復 福利 便送你六套視頻資源: Python、Java、Linux、Go、vue、react、javaScript