方法递归+组件递归实现json数据解析

开发过程中对于http请求,json数据是必不可少的处理,无论是前端向后端传参还是后端返回参数,现有技术的流行都是json数据的格式输出,那么对于复杂json数据的直观展示无疑可以提升我们开发及解决问题的效率,话不多说,我们来实现一个通过递归方式实现的一个json数据解析的小工具
首先,我们需要一个父组件:FormatJson.vue

<template>
    <div class="monitor-analysis-page">
        <div class="monitor-data-input">
            <el-input type="textarea" v-model="logData" :autosize="{ minRows: 6, maxRows: 20 }"
                placeholder=" Please Input Log Data" @input="dataBlur"></el-input>
            <span v-if="isWrong" class="data-tips">数据格式错误</span>
        </div>
        <div class="data-table">
            <ParamsTable @updateShowStatus="updateShowStatus" :tableData="tableDataArray" :key="setKey" />
        </div>
    </div>
</template>

<script>
import ParamsTable from '../../components/paramsTable/index.vue'
export default {
    components: {
        ParamsTable
    },
    data () {
        return {
            setKey: 0, // 用于更新视图的key
            isWrong: false,
            logData: '', // json原始数据
            tableDataArray: [] // 用于展示在表格的数据
        }
    },
    methods: {
        dataBlur (val) {
            if (!val) {
                this.tableDataArray = []
                this.isWrong = false
            }
            try { // 判断数据类型
                const jsonStr = JSON.parse(this.logData)
                console.log(jsonStr)
                this.tableDataArray = this.formatDataToArr(jsonStr, 1)
                this.setKey++
                this.isWrong = false
            } catch (error) {
                console.log(error)
                this.isWrong = true
            }
        },
        /**
        * 递归方法,将json数据解析为可展示在表格的数据结构
        */
        formatDataToArr (obj = {}, level = 1) {
            console.log('monitorData', obj)
            let tmpArr = []
            if (this.$proxy._typeof(obj) === 'array') {
                obj.forEach((o, i) => {
                    let indexObj = {}
                    indexObj.prop = i
                    indexObj.type = this.$proxy._typeof(o)
                    if (indexObj.type === 'object') {
                        indexObj.value = 'Object'
                        indexObj.level = level
                        indexObj.isShow = false
                        tmpArr.push(indexObj)
                        indexObj.children = this.formatDataToArr(o, level + 1)
                    } else {
                        indexObj.value = o
                        indexObj.level = level
                        tmpArr.push(indexObj)
                    }
                })
            } else {
                Object.keys(obj).map(k => {
                    let tmpObj = {}
                    tmpObj.prop = k
                    tmpObj.type = this.$proxy._typeof(obj[k])
                    tmpObj.value = obj[k]
                    tmpObj.level = level
                    console.log('tmpObj.type', tmpObj.type)
                    if (tmpObj.type === 'array' && tmpObj.value.length && this.$proxy._typeof(tmpObj.value[0]) === 'object') {
                        tmpObj.children = this.formatDataToArr(tmpObj.value, level + 1)
                        tmpObj.value = 'Array'
                        tmpObj.isShow = false
                    } else if (tmpObj.type === 'object') {
                        tmpObj.children = this.formatDataToArr(tmpObj.value, level + 1)
                        tmpObj.value = 'Object'
                        tmpObj.isShow = false
                    } else if (tmpObj.type === 'array' && tmpObj.value.length && this.$proxy._typeof(tmpObj.value[0]) === 'string') {
                        tmpObj.children = this.formatDataToArr(tmpObj.value, level + 1)
                        tmpObj.value = 'Array'
                        tmpObj.isShow = false
                    }
                    tmpArr.push(tmpObj)
                })
            }
            return tmpArr
        },
        updateShowStatus (o, i, isShow) {
            this.setKey++
            o.isShow = isShow
            console.log('tableDataArray', this.tableDataArray)
        }
    }
}
</script>

<style lang="scss" scoped>
@media (min-width: 1024px) {
    .monitor-analysis-page {
        width: 1024px;
    }
}
.monitor-analysis-page {
    margin: 30px auto;
    .data-tips {
        color: crimson;
    }
    .data-table {
        margin-top: 40px;
    }
}
</style>

还需要一个表格父组件:ParamsTable.vue

<template>
    <div class="tools-table-container">
        <div class="table-header-container">
            <div class="header-col">属性</div>
            <div class="header-col"></div>
            <div class="header-col">类型</div>
        </div>
        <div class="table-body-container">
            <div class="table-line-container" v-for="(o, i) in tableData" :key="i">
              <div class="line-type-include" :style="{'backgroundColor': colorList[o.level - 1]}" v-if="o.type === 'array' || o.type === 'object'">
                <div class="line-col">{{o.prop}}</div>
                <div class="line-col">{{o.value}} <el-button type="text" size="small" class="expand-fold" @click="changeStatus(o, i)">{{o.isShow ? '折叠' : '展开'}}</el-button></div>
                <div class="line-col">{{o.type}}</div>
              </div>
              <div class="line-type-include" :style="{'backgroundColor': colorList[o.level - 1]}" v-else>
                <div class="line-col">{{o.prop}}</div>
                <div class="line-col">{{o.value}}</div>
                <div class="line-col">{{o.type}}</div>
              </div>
              <tableBody @updateShowStatus="sendToParentShowStatus" :tableData="o.children" v-if="o.children && o.children.length && o.isShow === true"></tableBody>
            </div>
        </div>
    </div>
</template>
<script>
import tableBody from '@/components/paramsTable/tableBody.vue'

export default {
  components: {
    tableBody
  },
  props: {
    tableData: {
      type: Array,
      default: () => {
        return []
      }
    }
  },
  data () {
    return {
      colorList: ['#FFF3E6', '#FFE8D9', '#FFDDD0', '#FFD2C2', '#FFC7B5', '#FFBCA8'] // 用于表格背景色以区分数据的不同层级
    }
  },
  methods: {
    changeStatus (o, i) { // 通知父组件更新数据及视图
      console.log(o)
      this.$emit('updateShowStatus', o, i, !o.isShow)
    },
    sendToParentShowStatus (o, i, isShow) {
      this.$emit('updateShowStatus', o, i, isShow)
    }
  }
}
</script>
<style lang="scss" scoped>
.tools-table-container {
    width: 1024px;
    border: 1px solid #d3d7d4;
    .table-header-container {
        width: 100%;
        height: 40px;
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        justify-content: space-between;
        box-sizing: border-box;
        background-color: #B0E0E6;
        .header-col {
            // width: 50%;
            text-align: center;
            line-height: 40px;
        }
        .header-col {
            border-right: 1px solid #d3d7d4;
        }
        .header-col:last-child {
            border-right: none;
        }
    }
    .table-body-container {
        .table-line-container {
          .line-type-include {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            min-height: 30px;
            border-bottom: 1px solid #CCCCCC;
            .line-col {
              // overflow: auto;
              flex-grow: 1;
              // box-sizing: border-box;
              word-break: break-all;
              text-align: center;
              line-height: 25px;
              border-right: 1px solid #CCCCCC;
              color: #000000;
              position: relative;
              .expand-fold {
                position: absolute;
                right: 20px;
                top: 5px;
              }
            }
          }
        }
    }
}
</style>

一个表格子组件:TableBody.vue,这里注意不要省略组件的name属性,因为它引用了自己

<template>
    <div>
        <div class="table-line-container" v-for="(o, i) in tableData" :key="i">
            <div class="line-type-include" :style="{ 'backgroundColor': colorList[o.level - 1] }"
                v-if="o.type === 'array' || o.type === 'object'">
                <div class="line-col">{{ o.prop }}</div>
                <div class="line-col">{{ o.value }} <el-button class="expand-fold" size="small" type="text"
                        @click="changeShowStatus(o, i)">{{ o.isShow ? '折叠' : '展开' }}</el-button></div>
                <div class="line-col">{{ o.type }}</div>
            </div>
            <div class="line-type-include" :style="{ 'backgroundColor': colorList[o.level - 1] }" v-else>
                <div class="line-col">{{ o.prop }}</div>
                <div class="line-col">{{ o.value }}</div>
                <div class="line-col">{{ o.type }}</div>
            </div>
            <tableBody @updateShowStatus="sendToParentShowStatus" :tableData="o.children"
                v-if="o.children && o.children.length && o.isShow === true"></tableBody>
        </div>
    </div>
</template>
<script>
export default {
    name: 'tableBody',
    props: {
        tableData: {
            type: Array,
            default: () => {
                return []
            }
        }
    },
    data () {
        return {
            colorList: ['#FFF3E6', '#FFE8D9', '#FFDDD0', '#FFD2C2', '#FFC7B5', '#FFBCA8']
        }
    },
    methods: {
        sendToParentShowStatus (o, i) {
            this.$emit('updateShowStatus', o, i, !o.isShow)
        },
        changeShowStatus (o, i) {
            console.log(o)
            this.$emit('updateShowStatus', o, i, !o.isShow)
        }
    }
}
</script>
<style lang="scss" scoped>
.table-line-container {
    .line-type-include {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        min-height: 30px;
        border-bottom: 1px solid #CCCCCC;
        .line-col {
            // overflow: auto;
            flex-grow: 1;
            // box-sizing: border-box;
            word-break: break-all;
            text-align: center;
            line-height: 25px;
            border-right: 1px solid #CCCCCC;
            color: #000000;
            position: relative;
            .expand-fold {
                position: absolute;
                right: 20px;
                top: 5px;
            }
        }
    }
}
</style>

至此结束,点击 查看效果!

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值