複雜表格設計數據格式

1. 表頭設計

原理:
和多叉樹的原理類似,參考了它的展示形式。
多叉樹.png

表頭說明:
如果沒有孩子節點就只返回如下一個字段:
- name :名字

如果有孩子節點,就把數據加在children裏面,層層嵌套,返回字段如下:

  • name :名字
  • children : 孩子節點

數據結構格式,參考如下代碼:

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',
                            },

                        ]
                    }
                ]
            }
        ];

表頭的寬高方面,前端計算,後端不用管,按照如下格式返回數據即可。

2. 表格數據格式

每一項按照表頭展示的順序返回,通過數組的形式
返回一個參數:
- bodyData:總數據

數據結構格式參考代碼如下:

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"], 
    ]

3. 效果

如上表頭與表格數據代碼生成的效果如圖:
效果.png

4. 代碼

語法高亮用到 codemirror 插件

/**
 * 遞歸遍歷 格式化數組
 * @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>

5. 效果鏈接:

效果鏈接如下:

複雜表格設計數據格式

動態效果:
動態效果.gif

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章